Merge "Save usercreated bubbles and re-bubble next time; add experiment debug logs"
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..b8e4531 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1865,6 +1865,7 @@
     field public static final int accessibilityActionPageLeft = 16908360; // 0x1020048
     field public static final int accessibilityActionPageRight = 16908361; // 0x1020049
     field public static final int accessibilityActionPageUp = 16908358; // 0x1020046
+    field public static final int accessibilityActionPressAndHold = 16908362; // 0x102004a
     field public static final int accessibilityActionScrollDown = 16908346; // 0x102003a
     field public static final int accessibilityActionScrollLeft = 16908345; // 0x1020039
     field public static final int accessibilityActionScrollRight = 16908347; // 0x102003b
@@ -11556,8 +11557,11 @@
     field public static final int STATUS_FAILURE_ABORTED = 3; // 0x3
     field public static final int STATUS_FAILURE_BLOCKED = 2; // 0x2
     field public static final int STATUS_FAILURE_CONFLICT = 5; // 0x5
+    field public static final int STATUS_FAILURE_ILLEGAL_STATE = 9; // 0x9
     field public static final int STATUS_FAILURE_INCOMPATIBLE = 7; // 0x7
     field public static final int STATUS_FAILURE_INVALID = 4; // 0x4
+    field public static final int STATUS_FAILURE_NAME_NOT_FOUND = 8; // 0x8
+    field public static final int STATUS_FAILURE_SECURITY = 10; // 0xa
     field public static final int STATUS_FAILURE_STORAGE = 6; // 0x6
     field public static final int STATUS_PENDING_USER_ACTION = -1; // 0xffffffff
     field public static final int STATUS_SUCCESS = 0; // 0x0
@@ -11579,6 +11583,7 @@
     method public void removeChildSessionId(int);
     method public void removeSplit(@NonNull String) throws java.io.IOException;
     method public void setStagingProgress(float);
+    method public void transfer(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
     method public void transfer(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
   }
 
@@ -22956,6 +22961,7 @@
     method public int getAccumulatedDeltaRangeState();
     method public double getAccumulatedDeltaRangeUncertaintyMeters();
     method public double getAutomaticGainControlLevelDb();
+    method @FloatRange(from=0, to=50) public double getBasebandCn0DbHz();
     method @Deprecated public long getCarrierCycles();
     method public float getCarrierFrequencyHz();
     method @Deprecated public double getCarrierPhase();
@@ -22973,6 +22979,7 @@
     method public int getSvid();
     method public double getTimeOffsetNanos();
     method public boolean hasAutomaticGainControlLevelDb();
+    method public boolean hasBasebandCn0DbHz();
     method @Deprecated public boolean hasCarrierCycles();
     method public boolean hasCarrierFrequencyHz();
     method @Deprecated public boolean hasCarrierPhase();
@@ -46031,6 +46038,193 @@
 
 }
 
+package android.telephony.ims {
+
+  public final class ImsReasonInfo implements android.os.Parcelable {
+    ctor public ImsReasonInfo(int, int, @Nullable String);
+    method public int describeContents();
+    method public int getCode();
+    method public int getExtraCode();
+    method @Nullable public String getExtraMessage();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int CODE_ACCESS_CLASS_BLOCKED = 1512; // 0x5e8
+    field public static final int CODE_ANSWERED_ELSEWHERE = 1014; // 0x3f6
+    field public static final int CODE_BLACKLISTED_CALL_ID = 506; // 0x1fa
+    field public static final int CODE_CALL_BARRED = 240; // 0xf0
+    field public static final int CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 1100; // 0x44c
+    field public static final int CODE_CALL_END_CAUSE_CALL_PULL = 1016; // 0x3f8
+    field public static final int CODE_CALL_PULL_OUT_OF_SYNC = 1015; // 0x3f7
+    field public static final int CODE_DATA_DISABLED = 1406; // 0x57e
+    field public static final int CODE_DATA_LIMIT_REACHED = 1405; // 0x57d
+    field public static final int CODE_DIAL_MODIFIED_TO_DIAL = 246; // 0xf6
+    field public static final int CODE_DIAL_MODIFIED_TO_DIAL_VIDEO = 247; // 0xf7
+    field public static final int CODE_DIAL_MODIFIED_TO_SS = 245; // 0xf5
+    field public static final int CODE_DIAL_MODIFIED_TO_USSD = 244; // 0xf4
+    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL = 248; // 0xf8
+    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 249; // 0xf9
+    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_SS = 250; // 0xfa
+    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_USSD = 251; // 0xfb
+    field public static final int CODE_ECBM_NOT_SUPPORTED = 901; // 0x385
+    field public static final int CODE_EMERGENCY_PERM_FAILURE = 364; // 0x16c
+    field public static final int CODE_EMERGENCY_TEMP_FAILURE = 363; // 0x16b
+    field public static final int CODE_EPDG_TUNNEL_ESTABLISH_FAILURE = 1400; // 0x578
+    field public static final int CODE_EPDG_TUNNEL_LOST_CONNECTION = 1402; // 0x57a
+    field public static final int CODE_EPDG_TUNNEL_REKEY_FAILURE = 1401; // 0x579
+    field public static final int CODE_FDN_BLOCKED = 241; // 0xf1
+    field public static final int CODE_IKEV2_AUTH_FAILURE = 1408; // 0x580
+    field public static final int CODE_IMEI_NOT_ACCEPTED = 243; // 0xf3
+    field public static final int CODE_IWLAN_DPD_FAILURE = 1300; // 0x514
+    field public static final int CODE_LOCAL_CALL_BUSY = 142; // 0x8e
+    field public static final int CODE_LOCAL_CALL_CS_RETRY_REQUIRED = 146; // 0x92
+    field public static final int CODE_LOCAL_CALL_DECLINE = 143; // 0x8f
+    field public static final int CODE_LOCAL_CALL_EXCEEDED = 141; // 0x8d
+    field public static final int CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 145; // 0x91
+    field public static final int CODE_LOCAL_CALL_TERMINATED = 148; // 0x94
+    field public static final int CODE_LOCAL_CALL_VCC_ON_PROGRESSING = 144; // 0x90
+    field public static final int CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED = 147; // 0x93
+    field public static final int CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE = 108; // 0x6c
+    field public static final int CODE_LOCAL_HO_NOT_FEASIBLE = 149; // 0x95
+    field public static final int CODE_LOCAL_ILLEGAL_ARGUMENT = 101; // 0x65
+    field public static final int CODE_LOCAL_ILLEGAL_STATE = 102; // 0x66
+    field public static final int CODE_LOCAL_IMS_SERVICE_DOWN = 106; // 0x6a
+    field public static final int CODE_LOCAL_INTERNAL_ERROR = 103; // 0x67
+    field public static final int CODE_LOCAL_LOW_BATTERY = 112; // 0x70
+    field public static final int CODE_LOCAL_NETWORK_IP_CHANGED = 124; // 0x7c
+    field public static final int CODE_LOCAL_NETWORK_NO_LTE_COVERAGE = 122; // 0x7a
+    field public static final int CODE_LOCAL_NETWORK_NO_SERVICE = 121; // 0x79
+    field public static final int CODE_LOCAL_NETWORK_ROAMING = 123; // 0x7b
+    field public static final int CODE_LOCAL_NOT_REGISTERED = 132; // 0x84
+    field public static final int CODE_LOCAL_NO_PENDING_CALL = 107; // 0x6b
+    field public static final int CODE_LOCAL_POWER_OFF = 111; // 0x6f
+    field public static final int CODE_LOCAL_SERVICE_UNAVAILABLE = 131; // 0x83
+    field public static final int CODE_LOW_BATTERY = 505; // 0x1f9
+    field public static final int CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED = 1403; // 0x57b
+    field public static final int CODE_MEDIA_INIT_FAILED = 401; // 0x191
+    field public static final int CODE_MEDIA_NOT_ACCEPTABLE = 403; // 0x193
+    field public static final int CODE_MEDIA_NO_DATA = 402; // 0x192
+    field public static final int CODE_MEDIA_UNSPECIFIED = 404; // 0x194
+    field public static final int CODE_MULTIENDPOINT_NOT_SUPPORTED = 902; // 0x386
+    field public static final int CODE_NETWORK_DETACH = 1513; // 0x5e9
+    field public static final int CODE_NETWORK_REJECT = 1504; // 0x5e0
+    field public static final int CODE_NETWORK_RESP_TIMEOUT = 1503; // 0x5df
+    field public static final int CODE_NO_CSFB_IN_CS_ROAM = 1516; // 0x5ec
+    field public static final int CODE_NO_VALID_SIM = 1501; // 0x5dd
+    field public static final int CODE_OEM_CAUSE_1 = 61441; // 0xf001
+    field public static final int CODE_OEM_CAUSE_10 = 61450; // 0xf00a
+    field public static final int CODE_OEM_CAUSE_11 = 61451; // 0xf00b
+    field public static final int CODE_OEM_CAUSE_12 = 61452; // 0xf00c
+    field public static final int CODE_OEM_CAUSE_13 = 61453; // 0xf00d
+    field public static final int CODE_OEM_CAUSE_14 = 61454; // 0xf00e
+    field public static final int CODE_OEM_CAUSE_15 = 61455; // 0xf00f
+    field public static final int CODE_OEM_CAUSE_2 = 61442; // 0xf002
+    field public static final int CODE_OEM_CAUSE_3 = 61443; // 0xf003
+    field public static final int CODE_OEM_CAUSE_4 = 61444; // 0xf004
+    field public static final int CODE_OEM_CAUSE_5 = 61445; // 0xf005
+    field public static final int CODE_OEM_CAUSE_6 = 61446; // 0xf006
+    field public static final int CODE_OEM_CAUSE_7 = 61447; // 0xf007
+    field public static final int CODE_OEM_CAUSE_8 = 61448; // 0xf008
+    field public static final int CODE_OEM_CAUSE_9 = 61449; // 0xf009
+    field public static final int CODE_RADIO_ACCESS_FAILURE = 1505; // 0x5e1
+    field public static final int CODE_RADIO_INTERNAL_ERROR = 1502; // 0x5de
+    field public static final int CODE_RADIO_LINK_FAILURE = 1506; // 0x5e2
+    field public static final int CODE_RADIO_LINK_LOST = 1507; // 0x5e3
+    field public static final int CODE_RADIO_OFF = 1500; // 0x5dc
+    field public static final int CODE_RADIO_RELEASE_ABNORMAL = 1511; // 0x5e7
+    field public static final int CODE_RADIO_RELEASE_NORMAL = 1510; // 0x5e6
+    field public static final int CODE_RADIO_SETUP_FAILURE = 1509; // 0x5e5
+    field public static final int CODE_RADIO_UPLINK_FAILURE = 1508; // 0x5e4
+    field public static final int CODE_REGISTRATION_ERROR = 1000; // 0x3e8
+    field public static final int CODE_REJECTED_ELSEWHERE = 1017; // 0x3f9
+    field public static final int CODE_REJECT_1X_COLLISION = 1603; // 0x643
+    field public static final int CODE_REJECT_CALL_ON_OTHER_SUB = 1602; // 0x642
+    field public static final int CODE_REJECT_CALL_TYPE_NOT_ALLOWED = 1605; // 0x645
+    field public static final int CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED = 1617; // 0x651
+    field public static final int CODE_REJECT_INTERNAL_ERROR = 1612; // 0x64c
+    field public static final int CODE_REJECT_MAX_CALL_LIMIT_REACHED = 1608; // 0x648
+    field public static final int CODE_REJECT_ONGOING_CALL_SETUP = 1607; // 0x647
+    field public static final int CODE_REJECT_ONGOING_CALL_TRANSFER = 1611; // 0x64b
+    field public static final int CODE_REJECT_ONGOING_CALL_UPGRADE = 1616; // 0x650
+    field public static final int CODE_REJECT_ONGOING_CALL_WAITING_DISABLED = 1601; // 0x641
+    field public static final int CODE_REJECT_ONGOING_CONFERENCE_CALL = 1618; // 0x652
+    field public static final int CODE_REJECT_ONGOING_CS_CALL = 1621; // 0x655
+    field public static final int CODE_REJECT_ONGOING_E911_CALL = 1606; // 0x646
+    field public static final int CODE_REJECT_ONGOING_ENCRYPTED_CALL = 1620; // 0x654
+    field public static final int CODE_REJECT_ONGOING_HANDOVER = 1614; // 0x64e
+    field public static final int CODE_REJECT_QOS_FAILURE = 1613; // 0x64d
+    field public static final int CODE_REJECT_SERVICE_NOT_REGISTERED = 1604; // 0x644
+    field public static final int CODE_REJECT_UNKNOWN = 1600; // 0x640
+    field public static final int CODE_REJECT_UNSUPPORTED_SDP_HEADERS = 1610; // 0x64a
+    field public static final int CODE_REJECT_UNSUPPORTED_SIP_HEADERS = 1609; // 0x649
+    field public static final int CODE_REJECT_VT_AVPF_NOT_ALLOWED = 1619; // 0x653
+    field public static final int CODE_REJECT_VT_TTY_NOT_ALLOWED = 1615; // 0x64f
+    field public static final int CODE_REMOTE_CALL_DECLINE = 1404; // 0x57c
+    field public static final int CODE_SESSION_MODIFICATION_FAILED = 1517; // 0x5ed
+    field public static final int CODE_SIP_ALTERNATE_EMERGENCY_CALL = 1514; // 0x5ea
+    field public static final int CODE_SIP_AMBIGUOUS = 376; // 0x178
+    field public static final int CODE_SIP_BAD_ADDRESS = 337; // 0x151
+    field public static final int CODE_SIP_BAD_REQUEST = 331; // 0x14b
+    field public static final int CODE_SIP_BUSY = 338; // 0x152
+    field public static final int CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST = 372; // 0x174
+    field public static final int CODE_SIP_CLIENT_ERROR = 342; // 0x156
+    field public static final int CODE_SIP_EXTENSION_REQUIRED = 370; // 0x172
+    field public static final int CODE_SIP_FORBIDDEN = 332; // 0x14c
+    field public static final int CODE_SIP_GLOBAL_ERROR = 362; // 0x16a
+    field public static final int CODE_SIP_INTERVAL_TOO_BRIEF = 371; // 0x173
+    field public static final int CODE_SIP_LOOP_DETECTED = 373; // 0x175
+    field public static final int CODE_SIP_METHOD_NOT_ALLOWED = 366; // 0x16e
+    field public static final int CODE_SIP_NOT_ACCEPTABLE = 340; // 0x154
+    field public static final int CODE_SIP_NOT_FOUND = 333; // 0x14d
+    field public static final int CODE_SIP_NOT_REACHABLE = 341; // 0x155
+    field public static final int CODE_SIP_NOT_SUPPORTED = 334; // 0x14e
+    field public static final int CODE_SIP_PROXY_AUTHENTICATION_REQUIRED = 367; // 0x16f
+    field public static final int CODE_SIP_REDIRECTED = 321; // 0x141
+    field public static final int CODE_SIP_REQUEST_CANCELLED = 339; // 0x153
+    field public static final int CODE_SIP_REQUEST_ENTITY_TOO_LARGE = 368; // 0x170
+    field public static final int CODE_SIP_REQUEST_PENDING = 377; // 0x179
+    field public static final int CODE_SIP_REQUEST_TIMEOUT = 335; // 0x14f
+    field public static final int CODE_SIP_REQUEST_URI_TOO_LARGE = 369; // 0x171
+    field public static final int CODE_SIP_SERVER_ERROR = 354; // 0x162
+    field public static final int CODE_SIP_SERVER_INTERNAL_ERROR = 351; // 0x15f
+    field public static final int CODE_SIP_SERVER_TIMEOUT = 353; // 0x161
+    field public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352; // 0x160
+    field public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336; // 0x150
+    field public static final int CODE_SIP_TOO_MANY_HOPS = 374; // 0x176
+    field public static final int CODE_SIP_TRANSACTION_DOES_NOT_EXIST = 343; // 0x157
+    field public static final int CODE_SIP_UNDECIPHERABLE = 378; // 0x17a
+    field public static final int CODE_SIP_USER_MARKED_UNWANTED = 365; // 0x16d
+    field public static final int CODE_SIP_USER_REJECTED = 361; // 0x169
+    field public static final int CODE_SUPP_SVC_CANCELLED = 1202; // 0x4b2
+    field public static final int CODE_SUPP_SVC_FAILED = 1201; // 0x4b1
+    field public static final int CODE_SUPP_SVC_REINVITE_COLLISION = 1203; // 0x4b3
+    field public static final int CODE_TIMEOUT_1XX_WAITING = 201; // 0xc9
+    field public static final int CODE_TIMEOUT_NO_ANSWER = 202; // 0xca
+    field public static final int CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE = 203; // 0xcb
+    field public static final int CODE_UNSPECIFIED = 0; // 0x0
+    field public static final int CODE_USER_CANCELLED_SESSION_MODIFICATION = 512; // 0x200
+    field public static final int CODE_USER_DECLINE = 504; // 0x1f8
+    field public static final int CODE_USER_IGNORE = 503; // 0x1f7
+    field public static final int CODE_USER_NOANSWER = 502; // 0x1f6
+    field public static final int CODE_USER_REJECTED_SESSION_MODIFICATION = 511; // 0x1ff
+    field public static final int CODE_USER_TERMINATED = 501; // 0x1f5
+    field public static final int CODE_USER_TERMINATED_BY_REMOTE = 510; // 0x1fe
+    field public static final int CODE_UT_CB_PASSWORD_MISMATCH = 821; // 0x335
+    field public static final int CODE_UT_NETWORK_ERROR = 804; // 0x324
+    field public static final int CODE_UT_NOT_SUPPORTED = 801; // 0x321
+    field public static final int CODE_UT_OPERATION_NOT_ALLOWED = 803; // 0x323
+    field public static final int CODE_UT_SERVICE_UNAVAILABLE = 802; // 0x322
+    field public static final int CODE_UT_SS_MODIFIED_TO_DIAL = 822; // 0x336
+    field public static final int CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO = 825; // 0x339
+    field public static final int CODE_UT_SS_MODIFIED_TO_SS = 824; // 0x338
+    field public static final int CODE_UT_SS_MODIFIED_TO_USSD = 823; // 0x337
+    field public static final int CODE_WIFI_LOST = 1407; // 0x57f
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsReasonInfo> CREATOR;
+    field public static final int EXTRA_CODE_CALL_RETRY_BY_SETTINGS = 3; // 0x3
+    field public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1; // 0x1
+    field public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2; // 0x2
+  }
+
+}
+
 package android.telephony.mbms {
 
   public class DownloadProgressListener {
@@ -52811,6 +53005,7 @@
     field public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
     field public static final String ACTION_ARGUMENT_MOVE_WINDOW_X = "ACTION_ARGUMENT_MOVE_WINDOW_X";
     field public static final String ACTION_ARGUMENT_MOVE_WINDOW_Y = "ACTION_ARGUMENT_MOVE_WINDOW_Y";
+    field public static final String ACTION_ARGUMENT_PRESS_HOLD_DURATION_MILLIS_INT = "android.view.accessibility.action.ARGUMENT_PRESS_HOLD_DURATION_MILLIS_INT";
     field public static final String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
     field public static final String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
     field public static final String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
@@ -52878,6 +53073,7 @@
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PAGE_RIGHT;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PAGE_UP;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PASTE;
+    field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PRESS_AND_HOLD;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PREVIOUS_HTML_ELEMENT;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_BACKWARD;
diff --git a/api/system-current.txt b/api/system-current.txt
index 189585e..9c8bce6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -58,7 +58,7 @@
     field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
     field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
     field public static final String CONFIGURE_WIFI_DISPLAY = "android.permission.CONFIGURE_WIFI_DISPLAY";
-    field public static final String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL";
+    field @Deprecated public static final String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL";
     field public static final String CONNECTIVITY_USE_RESTRICTED_NETWORKS = "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS";
     field public static final String CONTROL_DISPLAY_COLOR_TRANSFORMS = "android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS";
     field public static final String CONTROL_DISPLAY_SATURATION = "android.permission.CONTROL_DISPLAY_SATURATION";
@@ -126,6 +126,7 @@
     field public static final String MODIFY_QUIET_MODE = "android.permission.MODIFY_QUIET_MODE";
     field public static final String MOVE_PACKAGE = "android.permission.MOVE_PACKAGE";
     field public static final String NETWORK_CARRIER_PROVISIONING = "android.permission.NETWORK_CARRIER_PROVISIONING";
+    field public static final String NETWORK_FACTORY = "android.permission.NETWORK_FACTORY";
     field public static final String NETWORK_MANAGED_PROVISIONING = "android.permission.NETWORK_MANAGED_PROVISIONING";
     field public static final String NETWORK_SCAN = "android.permission.NETWORK_SCAN";
     field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
@@ -135,6 +136,7 @@
     field public static final String NOTIFICATION_DURING_SETUP = "android.permission.NOTIFICATION_DURING_SETUP";
     field public static final String NOTIFY_TV_INPUTS = "android.permission.NOTIFY_TV_INPUTS";
     field public static final String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE";
+    field public static final String OBSERVE_NETWORK_POLICY = "android.permission.OBSERVE_NETWORK_POLICY";
     field public static final String OBSERVE_ROLE_HOLDERS = "android.permission.OBSERVE_ROLE_HOLDERS";
     field public static final String OPEN_ACCESSIBILITY_DETAILS_SETTINGS = "android.permission.OPEN_ACCESSIBILITY_DETAILS_SETTINGS";
     field public static final String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG";
@@ -1339,10 +1341,14 @@
     method public boolean isBleScanAlwaysAvailable();
     method public boolean isLeEnabled();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean removeOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setActiveDevice(@Nullable android.bluetooth.BluetoothDevice, int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean setScanMode(int, int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean setScanMode(int);
     field public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
     field public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE";
+    field public static final int ACTIVE_DEVICE_ALL = 2; // 0x2
+    field public static final int ACTIVE_DEVICE_AUDIO = 0; // 0x0
+    field public static final int ACTIVE_DEVICE_PHONE_CALL = 1; // 0x1
   }
 
   public static interface BluetoothAdapter.OnMetadataChangedListener {
@@ -4342,7 +4348,7 @@
   public class ConnectivityManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull android.os.ParcelFileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
     method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
-    method @RequiresPermission(android.Manifest.permission.LOCAL_MAC_ADDRESS) public String getCaptivePortalServerUrl();
+    method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCaptivePortalServerUrl();
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
@@ -5521,6 +5527,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 +6054,7 @@
 
   public final class BugreportManager {
     method @RequiresPermission(android.Manifest.permission.DUMP) public void cancelBugreport();
+    method @RequiresPermission(android.Manifest.permission.DUMP) public void requestBugreport(@NonNull android.os.BugreportParams, @Nullable CharSequence, @Nullable CharSequence);
     method @RequiresPermission(android.Manifest.permission.DUMP) public void startBugreport(@NonNull android.os.ParcelFileDescriptor, @Nullable android.os.ParcelFileDescriptor, @NonNull android.os.BugreportParams, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback);
   }
 
@@ -6492,6 +6500,7 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.PersistableBundle getSeedAccountOptions();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountType();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public long[] getSerialNumbersOfUsers(boolean);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserHandle> getUserHandles(boolean);
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public android.graphics.Bitmap getUserIcon();
     method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int getUserRestrictionSource(String, android.os.UserHandle);
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle);
@@ -7026,6 +7035,7 @@
     field public static final String ACTION_ACCESSIBILITY_DETAILS_SETTINGS = "android.settings.ACCESSIBILITY_DETAILS_SETTINGS";
     field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
     field public static final String ACTION_LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS = "android.settings.LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS";
+    field public static final String ACTION_MANAGE_APP_OVERLAY_PERMISSION = "android.settings.MANAGE_APP_OVERLAY_PERMISSION";
     field public static final String ACTION_MANAGE_DOMAIN_URLS = "android.settings.MANAGE_DOMAIN_URLS";
     field public static final String ACTION_MANAGE_MORE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_MORE_DEFAULT_APPS_SETTINGS";
     field public static final String ACTION_NOTIFICATION_POLICY_ACCESS_DETAIL_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_DETAIL_SETTINGS";
@@ -10076,186 +10086,6 @@
   }
 
   public final class ImsReasonInfo implements android.os.Parcelable {
-    ctor public ImsReasonInfo(int, int, String);
-    method public int describeContents();
-    method public int getCode();
-    method public int getExtraCode();
-    method public String getExtraMessage();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final int CODE_ACCESS_CLASS_BLOCKED = 1512; // 0x5e8
-    field public static final int CODE_ANSWERED_ELSEWHERE = 1014; // 0x3f6
-    field public static final int CODE_BLACKLISTED_CALL_ID = 506; // 0x1fa
-    field public static final int CODE_CALL_BARRED = 240; // 0xf0
-    field public static final int CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 1100; // 0x44c
-    field public static final int CODE_CALL_END_CAUSE_CALL_PULL = 1016; // 0x3f8
-    field public static final int CODE_CALL_PULL_OUT_OF_SYNC = 1015; // 0x3f7
-    field public static final int CODE_DATA_DISABLED = 1406; // 0x57e
-    field public static final int CODE_DATA_LIMIT_REACHED = 1405; // 0x57d
-    field public static final int CODE_DIAL_MODIFIED_TO_DIAL = 246; // 0xf6
-    field public static final int CODE_DIAL_MODIFIED_TO_DIAL_VIDEO = 247; // 0xf7
-    field public static final int CODE_DIAL_MODIFIED_TO_SS = 245; // 0xf5
-    field public static final int CODE_DIAL_MODIFIED_TO_USSD = 244; // 0xf4
-    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL = 248; // 0xf8
-    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 249; // 0xf9
-    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_SS = 250; // 0xfa
-    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_USSD = 251; // 0xfb
-    field public static final int CODE_ECBM_NOT_SUPPORTED = 901; // 0x385
-    field public static final int CODE_EMERGENCY_PERM_FAILURE = 364; // 0x16c
-    field public static final int CODE_EMERGENCY_TEMP_FAILURE = 363; // 0x16b
-    field public static final int CODE_EPDG_TUNNEL_ESTABLISH_FAILURE = 1400; // 0x578
-    field public static final int CODE_EPDG_TUNNEL_LOST_CONNECTION = 1402; // 0x57a
-    field public static final int CODE_EPDG_TUNNEL_REKEY_FAILURE = 1401; // 0x579
-    field public static final int CODE_FDN_BLOCKED = 241; // 0xf1
-    field public static final int CODE_IKEV2_AUTH_FAILURE = 1408; // 0x580
-    field public static final int CODE_IMEI_NOT_ACCEPTED = 243; // 0xf3
-    field public static final int CODE_IWLAN_DPD_FAILURE = 1300; // 0x514
-    field public static final int CODE_LOCAL_CALL_BUSY = 142; // 0x8e
-    field public static final int CODE_LOCAL_CALL_CS_RETRY_REQUIRED = 146; // 0x92
-    field public static final int CODE_LOCAL_CALL_DECLINE = 143; // 0x8f
-    field public static final int CODE_LOCAL_CALL_EXCEEDED = 141; // 0x8d
-    field public static final int CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 145; // 0x91
-    field public static final int CODE_LOCAL_CALL_TERMINATED = 148; // 0x94
-    field public static final int CODE_LOCAL_CALL_VCC_ON_PROGRESSING = 144; // 0x90
-    field public static final int CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED = 147; // 0x93
-    field public static final int CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE = 108; // 0x6c
-    field public static final int CODE_LOCAL_HO_NOT_FEASIBLE = 149; // 0x95
-    field public static final int CODE_LOCAL_ILLEGAL_ARGUMENT = 101; // 0x65
-    field public static final int CODE_LOCAL_ILLEGAL_STATE = 102; // 0x66
-    field public static final int CODE_LOCAL_IMS_SERVICE_DOWN = 106; // 0x6a
-    field public static final int CODE_LOCAL_INTERNAL_ERROR = 103; // 0x67
-    field public static final int CODE_LOCAL_LOW_BATTERY = 112; // 0x70
-    field public static final int CODE_LOCAL_NETWORK_IP_CHANGED = 124; // 0x7c
-    field public static final int CODE_LOCAL_NETWORK_NO_LTE_COVERAGE = 122; // 0x7a
-    field public static final int CODE_LOCAL_NETWORK_NO_SERVICE = 121; // 0x79
-    field public static final int CODE_LOCAL_NETWORK_ROAMING = 123; // 0x7b
-    field public static final int CODE_LOCAL_NOT_REGISTERED = 132; // 0x84
-    field public static final int CODE_LOCAL_NO_PENDING_CALL = 107; // 0x6b
-    field public static final int CODE_LOCAL_POWER_OFF = 111; // 0x6f
-    field public static final int CODE_LOCAL_SERVICE_UNAVAILABLE = 131; // 0x83
-    field public static final int CODE_LOW_BATTERY = 505; // 0x1f9
-    field public static final int CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED = 1403; // 0x57b
-    field public static final int CODE_MEDIA_INIT_FAILED = 401; // 0x191
-    field public static final int CODE_MEDIA_NOT_ACCEPTABLE = 403; // 0x193
-    field public static final int CODE_MEDIA_NO_DATA = 402; // 0x192
-    field public static final int CODE_MEDIA_UNSPECIFIED = 404; // 0x194
-    field public static final int CODE_MULTIENDPOINT_NOT_SUPPORTED = 902; // 0x386
-    field public static final int CODE_NETWORK_DETACH = 1513; // 0x5e9
-    field public static final int CODE_NETWORK_REJECT = 1504; // 0x5e0
-    field public static final int CODE_NETWORK_RESP_TIMEOUT = 1503; // 0x5df
-    field public static final int CODE_NO_CSFB_IN_CS_ROAM = 1516; // 0x5ec
-    field public static final int CODE_NO_VALID_SIM = 1501; // 0x5dd
-    field public static final int CODE_OEM_CAUSE_1 = 61441; // 0xf001
-    field public static final int CODE_OEM_CAUSE_10 = 61450; // 0xf00a
-    field public static final int CODE_OEM_CAUSE_11 = 61451; // 0xf00b
-    field public static final int CODE_OEM_CAUSE_12 = 61452; // 0xf00c
-    field public static final int CODE_OEM_CAUSE_13 = 61453; // 0xf00d
-    field public static final int CODE_OEM_CAUSE_14 = 61454; // 0xf00e
-    field public static final int CODE_OEM_CAUSE_15 = 61455; // 0xf00f
-    field public static final int CODE_OEM_CAUSE_2 = 61442; // 0xf002
-    field public static final int CODE_OEM_CAUSE_3 = 61443; // 0xf003
-    field public static final int CODE_OEM_CAUSE_4 = 61444; // 0xf004
-    field public static final int CODE_OEM_CAUSE_5 = 61445; // 0xf005
-    field public static final int CODE_OEM_CAUSE_6 = 61446; // 0xf006
-    field public static final int CODE_OEM_CAUSE_7 = 61447; // 0xf007
-    field public static final int CODE_OEM_CAUSE_8 = 61448; // 0xf008
-    field public static final int CODE_OEM_CAUSE_9 = 61449; // 0xf009
-    field public static final int CODE_RADIO_ACCESS_FAILURE = 1505; // 0x5e1
-    field public static final int CODE_RADIO_INTERNAL_ERROR = 1502; // 0x5de
-    field public static final int CODE_RADIO_LINK_FAILURE = 1506; // 0x5e2
-    field public static final int CODE_RADIO_LINK_LOST = 1507; // 0x5e3
-    field public static final int CODE_RADIO_OFF = 1500; // 0x5dc
-    field public static final int CODE_RADIO_RELEASE_ABNORMAL = 1511; // 0x5e7
-    field public static final int CODE_RADIO_RELEASE_NORMAL = 1510; // 0x5e6
-    field public static final int CODE_RADIO_SETUP_FAILURE = 1509; // 0x5e5
-    field public static final int CODE_RADIO_UPLINK_FAILURE = 1508; // 0x5e4
-    field public static final int CODE_REGISTRATION_ERROR = 1000; // 0x3e8
-    field public static final int CODE_REJECTED_ELSEWHERE = 1017; // 0x3f9
-    field public static final int CODE_REJECT_1X_COLLISION = 1603; // 0x643
-    field public static final int CODE_REJECT_CALL_ON_OTHER_SUB = 1602; // 0x642
-    field public static final int CODE_REJECT_CALL_TYPE_NOT_ALLOWED = 1605; // 0x645
-    field public static final int CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED = 1617; // 0x651
-    field public static final int CODE_REJECT_INTERNAL_ERROR = 1612; // 0x64c
-    field public static final int CODE_REJECT_MAX_CALL_LIMIT_REACHED = 1608; // 0x648
-    field public static final int CODE_REJECT_ONGOING_CALL_SETUP = 1607; // 0x647
-    field public static final int CODE_REJECT_ONGOING_CALL_TRANSFER = 1611; // 0x64b
-    field public static final int CODE_REJECT_ONGOING_CALL_UPGRADE = 1616; // 0x650
-    field public static final int CODE_REJECT_ONGOING_CALL_WAITING_DISABLED = 1601; // 0x641
-    field public static final int CODE_REJECT_ONGOING_CONFERENCE_CALL = 1618; // 0x652
-    field public static final int CODE_REJECT_ONGOING_CS_CALL = 1621; // 0x655
-    field public static final int CODE_REJECT_ONGOING_E911_CALL = 1606; // 0x646
-    field public static final int CODE_REJECT_ONGOING_ENCRYPTED_CALL = 1620; // 0x654
-    field public static final int CODE_REJECT_ONGOING_HANDOVER = 1614; // 0x64e
-    field public static final int CODE_REJECT_QOS_FAILURE = 1613; // 0x64d
-    field public static final int CODE_REJECT_SERVICE_NOT_REGISTERED = 1604; // 0x644
-    field public static final int CODE_REJECT_UNKNOWN = 1600; // 0x640
-    field public static final int CODE_REJECT_UNSUPPORTED_SDP_HEADERS = 1610; // 0x64a
-    field public static final int CODE_REJECT_UNSUPPORTED_SIP_HEADERS = 1609; // 0x649
-    field public static final int CODE_REJECT_VT_AVPF_NOT_ALLOWED = 1619; // 0x653
-    field public static final int CODE_REJECT_VT_TTY_NOT_ALLOWED = 1615; // 0x64f
-    field public static final int CODE_REMOTE_CALL_DECLINE = 1404; // 0x57c
-    field public static final int CODE_SESSION_MODIFICATION_FAILED = 1517; // 0x5ed
-    field public static final int CODE_SIP_ALTERNATE_EMERGENCY_CALL = 1514; // 0x5ea
-    field public static final int CODE_SIP_AMBIGUOUS = 376; // 0x178
-    field public static final int CODE_SIP_BAD_ADDRESS = 337; // 0x151
-    field public static final int CODE_SIP_BAD_REQUEST = 331; // 0x14b
-    field public static final int CODE_SIP_BUSY = 338; // 0x152
-    field public static final int CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST = 372; // 0x174
-    field public static final int CODE_SIP_CLIENT_ERROR = 342; // 0x156
-    field public static final int CODE_SIP_EXTENSION_REQUIRED = 370; // 0x172
-    field public static final int CODE_SIP_FORBIDDEN = 332; // 0x14c
-    field public static final int CODE_SIP_GLOBAL_ERROR = 362; // 0x16a
-    field public static final int CODE_SIP_INTERVAL_TOO_BRIEF = 371; // 0x173
-    field public static final int CODE_SIP_LOOP_DETECTED = 373; // 0x175
-    field public static final int CODE_SIP_METHOD_NOT_ALLOWED = 366; // 0x16e
-    field public static final int CODE_SIP_NOT_ACCEPTABLE = 340; // 0x154
-    field public static final int CODE_SIP_NOT_FOUND = 333; // 0x14d
-    field public static final int CODE_SIP_NOT_REACHABLE = 341; // 0x155
-    field public static final int CODE_SIP_NOT_SUPPORTED = 334; // 0x14e
-    field public static final int CODE_SIP_PROXY_AUTHENTICATION_REQUIRED = 367; // 0x16f
-    field public static final int CODE_SIP_REDIRECTED = 321; // 0x141
-    field public static final int CODE_SIP_REQUEST_CANCELLED = 339; // 0x153
-    field public static final int CODE_SIP_REQUEST_ENTITY_TOO_LARGE = 368; // 0x170
-    field public static final int CODE_SIP_REQUEST_PENDING = 377; // 0x179
-    field public static final int CODE_SIP_REQUEST_TIMEOUT = 335; // 0x14f
-    field public static final int CODE_SIP_REQUEST_URI_TOO_LARGE = 369; // 0x171
-    field public static final int CODE_SIP_SERVER_ERROR = 354; // 0x162
-    field public static final int CODE_SIP_SERVER_INTERNAL_ERROR = 351; // 0x15f
-    field public static final int CODE_SIP_SERVER_TIMEOUT = 353; // 0x161
-    field public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352; // 0x160
-    field public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336; // 0x150
-    field public static final int CODE_SIP_TOO_MANY_HOPS = 374; // 0x176
-    field public static final int CODE_SIP_TRANSACTION_DOES_NOT_EXIST = 343; // 0x157
-    field public static final int CODE_SIP_UNDECIPHERABLE = 378; // 0x17a
-    field public static final int CODE_SIP_USER_MARKED_UNWANTED = 365; // 0x16d
-    field public static final int CODE_SIP_USER_REJECTED = 361; // 0x169
-    field public static final int CODE_SUPP_SVC_CANCELLED = 1202; // 0x4b2
-    field public static final int CODE_SUPP_SVC_FAILED = 1201; // 0x4b1
-    field public static final int CODE_SUPP_SVC_REINVITE_COLLISION = 1203; // 0x4b3
-    field public static final int CODE_TIMEOUT_1XX_WAITING = 201; // 0xc9
-    field public static final int CODE_TIMEOUT_NO_ANSWER = 202; // 0xca
-    field public static final int CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE = 203; // 0xcb
-    field public static final int CODE_UNSPECIFIED = 0; // 0x0
-    field public static final int CODE_USER_CANCELLED_SESSION_MODIFICATION = 512; // 0x200
-    field public static final int CODE_USER_DECLINE = 504; // 0x1f8
-    field public static final int CODE_USER_IGNORE = 503; // 0x1f7
-    field public static final int CODE_USER_NOANSWER = 502; // 0x1f6
-    field public static final int CODE_USER_REJECTED_SESSION_MODIFICATION = 511; // 0x1ff
-    field public static final int CODE_USER_TERMINATED = 501; // 0x1f5
-    field public static final int CODE_USER_TERMINATED_BY_REMOTE = 510; // 0x1fe
-    field public static final int CODE_UT_CB_PASSWORD_MISMATCH = 821; // 0x335
-    field public static final int CODE_UT_NETWORK_ERROR = 804; // 0x324
-    field public static final int CODE_UT_NOT_SUPPORTED = 801; // 0x321
-    field public static final int CODE_UT_OPERATION_NOT_ALLOWED = 803; // 0x323
-    field public static final int CODE_UT_SERVICE_UNAVAILABLE = 802; // 0x322
-    field public static final int CODE_UT_SS_MODIFIED_TO_DIAL = 822; // 0x336
-    field public static final int CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO = 825; // 0x339
-    field public static final int CODE_UT_SS_MODIFIED_TO_SS = 824; // 0x338
-    field public static final int CODE_UT_SS_MODIFIED_TO_USSD = 823; // 0x337
-    field public static final int CODE_WIFI_LOST = 1407; // 0x57f
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsReasonInfo> CREATOR;
-    field public static final int EXTRA_CODE_CALL_RETRY_BY_SETTINGS = 3; // 0x3
-    field public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1; // 0x1
-    field public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2; // 0x2
     field public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED = "Forbidden. Not Authorized for Service";
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index 9ac789e..89fdb3f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1086,6 +1086,7 @@
     ctor public GnssMeasurement();
     method public void reset();
     method public void resetAutomaticGainControlLevel();
+    method public void resetBasebandCn0DbHz();
     method @Deprecated public void resetCarrierCycles();
     method public void resetCarrierFrequencyHz();
     method @Deprecated public void resetCarrierPhase();
@@ -1097,6 +1098,7 @@
     method public void setAccumulatedDeltaRangeState(int);
     method public void setAccumulatedDeltaRangeUncertaintyMeters(double);
     method public void setAutomaticGainControlLevelInDb(double);
+    method public void setBasebandCn0DbHz(double);
     method @Deprecated public void setCarrierCycles(long);
     method public void setCarrierFrequencyHz(float);
     method @Deprecated public void setCarrierPhase(double);
@@ -1754,6 +1756,7 @@
 
   public final class BugreportManager {
     method @RequiresPermission(android.Manifest.permission.DUMP) public void cancelBugreport();
+    method @RequiresPermission(android.Manifest.permission.DUMP) public void requestBugreport(@NonNull android.os.BugreportParams, @Nullable CharSequence, @Nullable CharSequence);
     method @RequiresPermission(android.Manifest.permission.DUMP) public void startBugreport(@NonNull android.os.ParcelFileDescriptor, @Nullable android.os.ParcelFileDescriptor, @NonNull android.os.BugreportParams, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback);
   }
 
@@ -2459,6 +2462,7 @@
 
   public final class Settings {
     field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
+    field public static final String ACTION_MANAGE_APP_OVERLAY_PERMISSION = "android.settings.MANAGE_APP_OVERLAY_PERMISSION";
     field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
     field public static final int RESET_MODE_PACKAGE_DEFAULTS = 1; // 0x1
   }
@@ -3450,190 +3454,6 @@
     ctor @Deprecated public ImsMmTelManager.RegistrationCallback();
   }
 
-  public final class ImsReasonInfo implements android.os.Parcelable {
-    ctor public ImsReasonInfo(int, int, String);
-    method public int describeContents();
-    method public int getCode();
-    method public int getExtraCode();
-    method public String getExtraMessage();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final int CODE_ACCESS_CLASS_BLOCKED = 1512; // 0x5e8
-    field public static final int CODE_ANSWERED_ELSEWHERE = 1014; // 0x3f6
-    field public static final int CODE_BLACKLISTED_CALL_ID = 506; // 0x1fa
-    field public static final int CODE_CALL_BARRED = 240; // 0xf0
-    field public static final int CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 1100; // 0x44c
-    field public static final int CODE_CALL_END_CAUSE_CALL_PULL = 1016; // 0x3f8
-    field public static final int CODE_CALL_PULL_OUT_OF_SYNC = 1015; // 0x3f7
-    field public static final int CODE_DATA_DISABLED = 1406; // 0x57e
-    field public static final int CODE_DATA_LIMIT_REACHED = 1405; // 0x57d
-    field public static final int CODE_DIAL_MODIFIED_TO_DIAL = 246; // 0xf6
-    field public static final int CODE_DIAL_MODIFIED_TO_DIAL_VIDEO = 247; // 0xf7
-    field public static final int CODE_DIAL_MODIFIED_TO_SS = 245; // 0xf5
-    field public static final int CODE_DIAL_MODIFIED_TO_USSD = 244; // 0xf4
-    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL = 248; // 0xf8
-    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 249; // 0xf9
-    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_SS = 250; // 0xfa
-    field public static final int CODE_DIAL_VIDEO_MODIFIED_TO_USSD = 251; // 0xfb
-    field public static final int CODE_ECBM_NOT_SUPPORTED = 901; // 0x385
-    field public static final int CODE_EMERGENCY_PERM_FAILURE = 364; // 0x16c
-    field public static final int CODE_EMERGENCY_TEMP_FAILURE = 363; // 0x16b
-    field public static final int CODE_EPDG_TUNNEL_ESTABLISH_FAILURE = 1400; // 0x578
-    field public static final int CODE_EPDG_TUNNEL_LOST_CONNECTION = 1402; // 0x57a
-    field public static final int CODE_EPDG_TUNNEL_REKEY_FAILURE = 1401; // 0x579
-    field public static final int CODE_FDN_BLOCKED = 241; // 0xf1
-    field public static final int CODE_IKEV2_AUTH_FAILURE = 1408; // 0x580
-    field public static final int CODE_IMEI_NOT_ACCEPTED = 243; // 0xf3
-    field public static final int CODE_IWLAN_DPD_FAILURE = 1300; // 0x514
-    field public static final int CODE_LOCAL_CALL_BUSY = 142; // 0x8e
-    field public static final int CODE_LOCAL_CALL_CS_RETRY_REQUIRED = 146; // 0x92
-    field public static final int CODE_LOCAL_CALL_DECLINE = 143; // 0x8f
-    field public static final int CODE_LOCAL_CALL_EXCEEDED = 141; // 0x8d
-    field public static final int CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 145; // 0x91
-    field public static final int CODE_LOCAL_CALL_TERMINATED = 148; // 0x94
-    field public static final int CODE_LOCAL_CALL_VCC_ON_PROGRESSING = 144; // 0x90
-    field public static final int CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED = 147; // 0x93
-    field public static final int CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE = 108; // 0x6c
-    field public static final int CODE_LOCAL_HO_NOT_FEASIBLE = 149; // 0x95
-    field public static final int CODE_LOCAL_ILLEGAL_ARGUMENT = 101; // 0x65
-    field public static final int CODE_LOCAL_ILLEGAL_STATE = 102; // 0x66
-    field public static final int CODE_LOCAL_IMS_SERVICE_DOWN = 106; // 0x6a
-    field public static final int CODE_LOCAL_INTERNAL_ERROR = 103; // 0x67
-    field public static final int CODE_LOCAL_LOW_BATTERY = 112; // 0x70
-    field public static final int CODE_LOCAL_NETWORK_IP_CHANGED = 124; // 0x7c
-    field public static final int CODE_LOCAL_NETWORK_NO_LTE_COVERAGE = 122; // 0x7a
-    field public static final int CODE_LOCAL_NETWORK_NO_SERVICE = 121; // 0x79
-    field public static final int CODE_LOCAL_NETWORK_ROAMING = 123; // 0x7b
-    field public static final int CODE_LOCAL_NOT_REGISTERED = 132; // 0x84
-    field public static final int CODE_LOCAL_NO_PENDING_CALL = 107; // 0x6b
-    field public static final int CODE_LOCAL_POWER_OFF = 111; // 0x6f
-    field public static final int CODE_LOCAL_SERVICE_UNAVAILABLE = 131; // 0x83
-    field public static final int CODE_LOW_BATTERY = 505; // 0x1f9
-    field public static final int CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED = 1403; // 0x57b
-    field public static final int CODE_MEDIA_INIT_FAILED = 401; // 0x191
-    field public static final int CODE_MEDIA_NOT_ACCEPTABLE = 403; // 0x193
-    field public static final int CODE_MEDIA_NO_DATA = 402; // 0x192
-    field public static final int CODE_MEDIA_UNSPECIFIED = 404; // 0x194
-    field public static final int CODE_MULTIENDPOINT_NOT_SUPPORTED = 902; // 0x386
-    field public static final int CODE_NETWORK_DETACH = 1513; // 0x5e9
-    field public static final int CODE_NETWORK_REJECT = 1504; // 0x5e0
-    field public static final int CODE_NETWORK_RESP_TIMEOUT = 1503; // 0x5df
-    field public static final int CODE_NO_CSFB_IN_CS_ROAM = 1516; // 0x5ec
-    field public static final int CODE_NO_VALID_SIM = 1501; // 0x5dd
-    field public static final int CODE_OEM_CAUSE_1 = 61441; // 0xf001
-    field public static final int CODE_OEM_CAUSE_10 = 61450; // 0xf00a
-    field public static final int CODE_OEM_CAUSE_11 = 61451; // 0xf00b
-    field public static final int CODE_OEM_CAUSE_12 = 61452; // 0xf00c
-    field public static final int CODE_OEM_CAUSE_13 = 61453; // 0xf00d
-    field public static final int CODE_OEM_CAUSE_14 = 61454; // 0xf00e
-    field public static final int CODE_OEM_CAUSE_15 = 61455; // 0xf00f
-    field public static final int CODE_OEM_CAUSE_2 = 61442; // 0xf002
-    field public static final int CODE_OEM_CAUSE_3 = 61443; // 0xf003
-    field public static final int CODE_OEM_CAUSE_4 = 61444; // 0xf004
-    field public static final int CODE_OEM_CAUSE_5 = 61445; // 0xf005
-    field public static final int CODE_OEM_CAUSE_6 = 61446; // 0xf006
-    field public static final int CODE_OEM_CAUSE_7 = 61447; // 0xf007
-    field public static final int CODE_OEM_CAUSE_8 = 61448; // 0xf008
-    field public static final int CODE_OEM_CAUSE_9 = 61449; // 0xf009
-    field public static final int CODE_RADIO_ACCESS_FAILURE = 1505; // 0x5e1
-    field public static final int CODE_RADIO_INTERNAL_ERROR = 1502; // 0x5de
-    field public static final int CODE_RADIO_LINK_FAILURE = 1506; // 0x5e2
-    field public static final int CODE_RADIO_LINK_LOST = 1507; // 0x5e3
-    field public static final int CODE_RADIO_OFF = 1500; // 0x5dc
-    field public static final int CODE_RADIO_RELEASE_ABNORMAL = 1511; // 0x5e7
-    field public static final int CODE_RADIO_RELEASE_NORMAL = 1510; // 0x5e6
-    field public static final int CODE_RADIO_SETUP_FAILURE = 1509; // 0x5e5
-    field public static final int CODE_RADIO_UPLINK_FAILURE = 1508; // 0x5e4
-    field public static final int CODE_REGISTRATION_ERROR = 1000; // 0x3e8
-    field public static final int CODE_REJECTED_ELSEWHERE = 1017; // 0x3f9
-    field public static final int CODE_REJECT_1X_COLLISION = 1603; // 0x643
-    field public static final int CODE_REJECT_CALL_ON_OTHER_SUB = 1602; // 0x642
-    field public static final int CODE_REJECT_CALL_TYPE_NOT_ALLOWED = 1605; // 0x645
-    field public static final int CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED = 1617; // 0x651
-    field public static final int CODE_REJECT_INTERNAL_ERROR = 1612; // 0x64c
-    field public static final int CODE_REJECT_MAX_CALL_LIMIT_REACHED = 1608; // 0x648
-    field public static final int CODE_REJECT_ONGOING_CALL_SETUP = 1607; // 0x647
-    field public static final int CODE_REJECT_ONGOING_CALL_TRANSFER = 1611; // 0x64b
-    field public static final int CODE_REJECT_ONGOING_CALL_UPGRADE = 1616; // 0x650
-    field public static final int CODE_REJECT_ONGOING_CALL_WAITING_DISABLED = 1601; // 0x641
-    field public static final int CODE_REJECT_ONGOING_CONFERENCE_CALL = 1618; // 0x652
-    field public static final int CODE_REJECT_ONGOING_CS_CALL = 1621; // 0x655
-    field public static final int CODE_REJECT_ONGOING_E911_CALL = 1606; // 0x646
-    field public static final int CODE_REJECT_ONGOING_ENCRYPTED_CALL = 1620; // 0x654
-    field public static final int CODE_REJECT_ONGOING_HANDOVER = 1614; // 0x64e
-    field public static final int CODE_REJECT_QOS_FAILURE = 1613; // 0x64d
-    field public static final int CODE_REJECT_SERVICE_NOT_REGISTERED = 1604; // 0x644
-    field public static final int CODE_REJECT_UNKNOWN = 1600; // 0x640
-    field public static final int CODE_REJECT_UNSUPPORTED_SDP_HEADERS = 1610; // 0x64a
-    field public static final int CODE_REJECT_UNSUPPORTED_SIP_HEADERS = 1609; // 0x649
-    field public static final int CODE_REJECT_VT_AVPF_NOT_ALLOWED = 1619; // 0x653
-    field public static final int CODE_REJECT_VT_TTY_NOT_ALLOWED = 1615; // 0x64f
-    field public static final int CODE_REMOTE_CALL_DECLINE = 1404; // 0x57c
-    field public static final int CODE_SESSION_MODIFICATION_FAILED = 1517; // 0x5ed
-    field public static final int CODE_SIP_ALTERNATE_EMERGENCY_CALL = 1514; // 0x5ea
-    field public static final int CODE_SIP_AMBIGUOUS = 376; // 0x178
-    field public static final int CODE_SIP_BAD_ADDRESS = 337; // 0x151
-    field public static final int CODE_SIP_BAD_REQUEST = 331; // 0x14b
-    field public static final int CODE_SIP_BUSY = 338; // 0x152
-    field public static final int CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST = 372; // 0x174
-    field public static final int CODE_SIP_CLIENT_ERROR = 342; // 0x156
-    field public static final int CODE_SIP_EXTENSION_REQUIRED = 370; // 0x172
-    field public static final int CODE_SIP_FORBIDDEN = 332; // 0x14c
-    field public static final int CODE_SIP_GLOBAL_ERROR = 362; // 0x16a
-    field public static final int CODE_SIP_INTERVAL_TOO_BRIEF = 371; // 0x173
-    field public static final int CODE_SIP_LOOP_DETECTED = 373; // 0x175
-    field public static final int CODE_SIP_METHOD_NOT_ALLOWED = 366; // 0x16e
-    field public static final int CODE_SIP_NOT_ACCEPTABLE = 340; // 0x154
-    field public static final int CODE_SIP_NOT_FOUND = 333; // 0x14d
-    field public static final int CODE_SIP_NOT_REACHABLE = 341; // 0x155
-    field public static final int CODE_SIP_NOT_SUPPORTED = 334; // 0x14e
-    field public static final int CODE_SIP_PROXY_AUTHENTICATION_REQUIRED = 367; // 0x16f
-    field public static final int CODE_SIP_REDIRECTED = 321; // 0x141
-    field public static final int CODE_SIP_REQUEST_CANCELLED = 339; // 0x153
-    field public static final int CODE_SIP_REQUEST_ENTITY_TOO_LARGE = 368; // 0x170
-    field public static final int CODE_SIP_REQUEST_PENDING = 377; // 0x179
-    field public static final int CODE_SIP_REQUEST_TIMEOUT = 335; // 0x14f
-    field public static final int CODE_SIP_REQUEST_URI_TOO_LARGE = 369; // 0x171
-    field public static final int CODE_SIP_SERVER_ERROR = 354; // 0x162
-    field public static final int CODE_SIP_SERVER_INTERNAL_ERROR = 351; // 0x15f
-    field public static final int CODE_SIP_SERVER_TIMEOUT = 353; // 0x161
-    field public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352; // 0x160
-    field public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336; // 0x150
-    field public static final int CODE_SIP_TOO_MANY_HOPS = 374; // 0x176
-    field public static final int CODE_SIP_TRANSACTION_DOES_NOT_EXIST = 343; // 0x157
-    field public static final int CODE_SIP_UNDECIPHERABLE = 378; // 0x17a
-    field public static final int CODE_SIP_USER_MARKED_UNWANTED = 365; // 0x16d
-    field public static final int CODE_SIP_USER_REJECTED = 361; // 0x169
-    field public static final int CODE_SUPP_SVC_CANCELLED = 1202; // 0x4b2
-    field public static final int CODE_SUPP_SVC_FAILED = 1201; // 0x4b1
-    field public static final int CODE_SUPP_SVC_REINVITE_COLLISION = 1203; // 0x4b3
-    field public static final int CODE_TIMEOUT_1XX_WAITING = 201; // 0xc9
-    field public static final int CODE_TIMEOUT_NO_ANSWER = 202; // 0xca
-    field public static final int CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE = 203; // 0xcb
-    field public static final int CODE_UNSPECIFIED = 0; // 0x0
-    field public static final int CODE_USER_CANCELLED_SESSION_MODIFICATION = 512; // 0x200
-    field public static final int CODE_USER_DECLINE = 504; // 0x1f8
-    field public static final int CODE_USER_IGNORE = 503; // 0x1f7
-    field public static final int CODE_USER_NOANSWER = 502; // 0x1f6
-    field public static final int CODE_USER_REJECTED_SESSION_MODIFICATION = 511; // 0x1ff
-    field public static final int CODE_USER_TERMINATED = 501; // 0x1f5
-    field public static final int CODE_USER_TERMINATED_BY_REMOTE = 510; // 0x1fe
-    field public static final int CODE_UT_CB_PASSWORD_MISMATCH = 821; // 0x335
-    field public static final int CODE_UT_NETWORK_ERROR = 804; // 0x324
-    field public static final int CODE_UT_NOT_SUPPORTED = 801; // 0x321
-    field public static final int CODE_UT_OPERATION_NOT_ALLOWED = 803; // 0x323
-    field public static final int CODE_UT_SERVICE_UNAVAILABLE = 802; // 0x322
-    field public static final int CODE_UT_SS_MODIFIED_TO_DIAL = 822; // 0x336
-    field public static final int CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO = 825; // 0x339
-    field public static final int CODE_UT_SS_MODIFIED_TO_SS = 824; // 0x338
-    field public static final int CODE_UT_SS_MODIFIED_TO_USSD = 823; // 0x337
-    field public static final int CODE_WIFI_LOST = 1407; // 0x57f
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsReasonInfo> CREATOR;
-    field public static final int EXTRA_CODE_CALL_RETRY_BY_SETTINGS = 3; // 0x3
-    field public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1; // 0x1
-    field public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2; // 0x2
-    field public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED = "Forbidden. Not Authorized for Service";
-  }
-
   public class ImsService extends android.app.Service {
     ctor public ImsService();
     method public android.telephony.ims.feature.MmTelFeature createMmTelFeature(int);
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/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
index 260c7df..df643831 100644
--- a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
+++ b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
@@ -16,6 +16,7 @@
 
 package android.app.timezonedetector;
 
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
 import android.app.timezonedetector.PhoneTimeZoneSuggestion;
 
 /**
@@ -32,5 +33,6 @@
  * {@hide}
  */
 interface ITimeZoneDetectorService {
+  void suggestManualTimeZone(in ManualTimeZoneSuggestion timeZoneSuggestion);
   void suggestPhoneTimeZone(in PhoneTimeZoneSuggestion timeZoneSuggestion);
 }
diff --git a/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.aidl b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.aidl
new file mode 100644
index 0000000..d1be86a
--- /dev/null
+++ b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timezonedetector;
+
+parcelable ManualTimeZoneSuggestion;
diff --git a/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
new file mode 100644
index 0000000..a6b953b
--- /dev/null
+++ b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timezonedetector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A time signal from a manual (user provided) source. The value consists of the number of
+ * milliseconds elapsed since 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime
+ * clock when that number was established. The elapsed realtime clock is considered accurate but
+ * volatile, so time signals must not be persisted across device resets.
+ *
+ * @hide
+ */
+public final class ManualTimeZoneSuggestion implements Parcelable {
+
+    public static final @NonNull Creator<ManualTimeZoneSuggestion> CREATOR =
+            new Creator<ManualTimeZoneSuggestion>() {
+                public ManualTimeZoneSuggestion createFromParcel(Parcel in) {
+                    return ManualTimeZoneSuggestion.createFromParcel(in);
+                }
+
+                public ManualTimeZoneSuggestion[] newArray(int size) {
+                    return new ManualTimeZoneSuggestion[size];
+                }
+            };
+
+    @NonNull
+    private final String mZoneId;
+    @Nullable
+    private ArrayList<String> mDebugInfo;
+
+    public ManualTimeZoneSuggestion(@NonNull String zoneId) {
+        mZoneId = Objects.requireNonNull(zoneId);
+    }
+
+    private static ManualTimeZoneSuggestion createFromParcel(Parcel in) {
+        String zoneId = in.readString();
+        ManualTimeZoneSuggestion suggestion = new ManualTimeZoneSuggestion(zoneId);
+        @SuppressWarnings("unchecked")
+        ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
+        suggestion.mDebugInfo = debugInfo;
+        return suggestion;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mZoneId);
+        dest.writeList(mDebugInfo);
+    }
+
+    @NonNull
+    public String getZoneId() {
+        return mZoneId;
+    }
+
+    @NonNull
+    public List<String> getDebugInfo() {
+        return mDebugInfo == null
+                ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
+    }
+
+    /**
+     * Associates information with the instance that can be useful for debugging / logging. The
+     * information is present in {@link #toString()} but is not considered for
+     * {@link #equals(Object)} and {@link #hashCode()}.
+     */
+    public void addDebugInfo(String... debugInfos) {
+        if (mDebugInfo == null) {
+            mDebugInfo = new ArrayList<>();
+        }
+        mDebugInfo.addAll(Arrays.asList(debugInfos));
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        ManualTimeZoneSuggestion
+                that = (ManualTimeZoneSuggestion) o;
+        return Objects.equals(mZoneId, that.mZoneId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mZoneId);
+    }
+
+    @Override
+    public String toString() {
+        return "ManualTimeSuggestion{"
+                + "mZoneId=" + mZoneId
+                + ", mDebugInfo=" + mDebugInfo
+                + '}';
+    }
+}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index ab404d2..fb5645a 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -160,48 +160,114 @@
     public static final int REASON_MAIN_MASK = 0xFF00;
     /** @hide */
     public static final int REASON_MAIN_DEFAULT =   0x0100;
-    /** @hide */
+    /**
+     * The app spent sufficient time in the old bucket without any substantial event so it reached
+     * the timeout threshold to have its bucket lowered.
+     *
+     * @hide
+     */
     public static final int REASON_MAIN_TIMEOUT =   0x0200;
-    /** @hide */
+    /**
+     * The app was used in some way. Look at the REASON_SUB_USAGE_ reason for more details.
+     * @hide
+     */
     public static final int REASON_MAIN_USAGE =     0x0300;
-    /** @hide */
+    /**
+     * Forced by a core UID.
+     * @hide
+     */
     public static final int REASON_MAIN_FORCED =    0x0400;
-    /** @hide */
+    /**
+     * Set by a privileged system app.
+     * @hide
+     */
     public static final int REASON_MAIN_PREDICTED = 0x0500;
 
     /** @hide */
     public static final int REASON_SUB_MASK = 0x00FF;
-    /** @hide */
+    /**
+     * The app was interacted with in some way by the system.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_SYSTEM_INTERACTION = 0x0001;
-    /** @hide */
+    /**
+     * A notification was viewed by the user. This does not mean the user interacted with the
+     * notification.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_NOTIFICATION_SEEN  = 0x0002;
-    /** @hide */
+    /**
+     * The app was interacted with in some way by the user. This includes interacting with
+     * notification.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_USER_INTERACTION   = 0x0003;
-    /** @hide */
+    /**
+     * An {@link android.app.Activity} moved to the foreground.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_MOVE_TO_FOREGROUND = 0x0004;
-    /** @hide */
+    /**
+     * An {@link android.app.Activity} moved to the background.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_MOVE_TO_BACKGROUND = 0x0005;
-    /** @hide */
+    /**
+     * There was a system update.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_SYSTEM_UPDATE      = 0x0006;
-    /** @hide */
+    /**
+     * An app is in an elevated bucket because of an active timeout preventing it from being placed
+     * in a lower bucket.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_ACTIVE_TIMEOUT     = 0x0007;
-    /** @hide */
+    /**
+     * This system package's sync adapter has been used for another package's content provider.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_SYNC_ADAPTER       = 0x0008;
-    /** @hide */
+    /**
+     * A slice was pinned by an app.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_SLICE_PINNED       = 0x0009;
-    /** @hide */
+    /** /**
+     * A slice was pinned by the default launcher or the default assistant.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_SLICE_PINNED_PRIV  = 0x000A;
-    /** @hide */
+    /**
+     * A sync operation that is exempt from app standby was scheduled when the device wasn't Dozing.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE = 0x000B;
-    /** @hide */
+    /**
+     * A sync operation that is exempt from app standby was scheduled while the device was Dozing.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE = 0x000C;
-    /** @hide */
+    /**
+     * A sync operation that is exempt from app standby started.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_EXEMPTED_SYNC_START = 0x000D;
-    /** @hide */
+    /**
+     * A sync operation that is not exempt from app standby was scheduled.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED = 0x000E;
-    /** @hide */
+    /**
+     * A foreground service started.
+     * @hide
+     */
     public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_START = 0x000F;
-    /** @hide */
+    /**
+     * The predicted bucket was restored after the app's temporary elevation to the ACTIVE bucket
+     * ended.
+     * @hide
+     */
     public static final int REASON_SUB_PREDICTED_RESTORED       = 0x0001;
 
 
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 9d152a7..89f9cbc 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -456,6 +456,37 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface IoCapability {}
 
+    /** @hide */
+    @IntDef(prefix = "ACTIVE_DEVICE_", value = {ACTIVE_DEVICE_AUDIO,
+            ACTIVE_DEVICE_PHONE_CALL, ACTIVE_DEVICE_ALL})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ActiveDeviceUse {}
+
+    /**
+     * Use the specified device for audio (a2dp and hearing aid profile)
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int ACTIVE_DEVICE_AUDIO = 0;
+
+    /**
+     * Use the specified device for phone calls (headset profile and hearing
+     * aid profile)
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int ACTIVE_DEVICE_PHONE_CALL = 1;
+
+    /**
+     * Use the specified device for a2dp, hearing aid profile, and headset profile
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int ACTIVE_DEVICE_ALL = 2;
+
     /**
      * Broadcast Action: The local Bluetooth adapter has started the remote
      * device discovery process.
@@ -1734,6 +1765,41 @@
     }
 
     /**
+     *
+     * @param device is the remote bluetooth device
+     * @param profiles represents the purpose for which we are setting this as the active device.
+     *                 Possible values are:
+     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_AUDIO},
+     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_PHONE_CALL},
+     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_ALL}
+     * @return false on immediate error, true otherwise
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    public boolean setActiveDevice(@Nullable BluetoothDevice device,
+            @ActiveDeviceUse int profiles) {
+        if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL
+                && profiles != ACTIVE_DEVICE_ALL) {
+            Log.e(TAG, "Invalid profiles param value in setActiveDevice");
+            return false;
+        }
+
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null) {
+                return mService.setActiveDevice(device, profiles);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+
+        return false;
+    }
+
+    /**
      * Connects all enabled and supported bluetooth profiles between the local and remote device
      *
      * @param device is the remote device with which to connect these profiles
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index 04e15c7..b740617 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -36,7 +36,7 @@
 
     void close();
     void commit(in IntentSender statusReceiver, boolean forTransferred);
-    void transfer(in String packageName);
+    void transfer(in String packageName, in IntentSender statusReceiver);
     void abandon();
 
     boolean isMultiPackage();
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 0c52979..4ab6f3c 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -29,6 +29,8 @@
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.PackageManager.DeleteFlags;
@@ -36,9 +38,11 @@
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.FileBridge;
 import android.os.Handler;
 import android.os.HandlerExecutor;
+import android.os.IBinder;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
@@ -67,6 +71,8 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.stream.Collectors;
 
@@ -176,7 +182,7 @@
      * {@link #STATUS_PENDING_USER_ACTION}, {@link #STATUS_SUCCESS},
      * {@link #STATUS_FAILURE}, {@link #STATUS_FAILURE_ABORTED},
      * {@link #STATUS_FAILURE_BLOCKED}, {@link #STATUS_FAILURE_CONFLICT},
-     * {@link #STATUS_FAILURE_INCOMPATIBLE}, {@link #STATUS_FAILURE_INVALID}, or
+     * {@link #STATUS_FAILURE_INCOMPATIBLE}, {@link #STATUS_FAILURE_INVALID},
      * {@link #STATUS_FAILURE_STORAGE}.
      * <p>
      * More information about a status may be available through additional
@@ -316,6 +322,34 @@
      */
     public static final int STATUS_FAILURE_INCOMPATIBLE = 7;
 
+    /**
+     * The transfer failed because a target package can't be found. For example
+     * transferring a session to a non-existing package.
+     * <p>
+     * The result may also contain {@link #EXTRA_OTHER_PACKAGE_NAME} with the
+     * missing package.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     * @see #EXTRA_OTHER_PACKAGE_NAME
+     */
+    public static final int STATUS_FAILURE_NAME_NOT_FOUND = 8;
+
+    /**
+     * The transfer failed because a session is in invalid state. For example
+     * transferring an already committed session.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     */
+    public static final int STATUS_FAILURE_ILLEGAL_STATE = 9;
+
+    /**
+     * The transfer failed for security reasons. For example transferring
+     * to a package which does not have INSTALL_PACKAGES permission.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     */
+    public static final int STATUS_FAILURE_SECURITY = 10;
+
     private final IPackageInstaller mInstaller;
     private final int mUserId;
     private final String mInstallerPackageName;
@@ -1052,7 +1086,8 @@
         }
 
         /**
-         * Attempt to commit a session that has been {@link #transfer(String) transferred}.
+         * Attempt to commit a session that has been {@link #transfer(String, IntentSender)
+         * transferred}.
          *
          * <p>If the device reboots before the session has been finalized, you may commit the
          * session again.
@@ -1093,6 +1128,38 @@
          *
          * @param packageName The package of the new owner. Needs to hold the INSTALL_PACKAGES
          *                    permission.
+         * @param statusReceiver Called when the state of the session changes. Intents sent to this
+         *                       receiver contain {@link #EXTRA_STATUS}. Refer to the individual
+         *                       transfer status codes on how to handle them.
+         *
+         * @throws PackageManager.NameNotFoundException if the new owner could not be found.
+         * @throws SecurityException if called after the session has been committed or abandoned.
+         * @throws SecurityException if the session does not update the original installer
+         * @throws SecurityException if streams opened through
+         *                           {@link #openWrite(String, long, long) are still open.
+         */
+        public void transfer(@NonNull String packageName, @NonNull IntentSender statusReceiver)
+                throws PackageManager.NameNotFoundException {
+            Preconditions.checkNotNull(statusReceiver);
+            Preconditions.checkNotNull(packageName);
+
+            try {
+                mSession.transfer(packageName, statusReceiver);
+            } catch (ParcelableException e) {
+                e.maybeRethrow(PackageManager.NameNotFoundException.class);
+                throw new RuntimeException(e);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Transfer the session to a new owner.
+         * This is a convenience blocking wrapper around {@link #transfer(String, IntentSender)}.
+         * Converts all statuses into exceptions.
+         *
+         * @param packageName The package of the new owner. Needs to hold the INSTALL_PACKAGES
+         *                    permission.
          *
          * @throws PackageManager.NameNotFoundException if the new owner could not be found.
          * @throws SecurityException if called after the session has been committed or abandoned.
@@ -1104,13 +1171,43 @@
                 throws PackageManager.NameNotFoundException {
             Preconditions.checkNotNull(packageName);
 
+            CompletableFuture<Intent> intentFuture = new CompletableFuture<Intent>();
             try {
-                mSession.transfer(packageName);
+                IIntentSender localSender = new IIntentSender.Stub() {
+                    @Override
+                    public void send(int code, Intent intent, String resolvedType,
+                            IBinder whitelistToken,
+                            IIntentReceiver finishedReceiver, String requiredPermission,
+                            Bundle options) {
+                        intentFuture.complete(intent);
+                    }
+                };
+                transfer(packageName, new IntentSender(localSender));
             } catch (ParcelableException e) {
                 e.maybeRethrow(PackageManager.NameNotFoundException.class);
                 throw new RuntimeException(e);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
+            }
+
+            try {
+                Intent intent = intentFuture.get();
+                final int status = intent.getIntExtra(EXTRA_STATUS, Integer.MIN_VALUE);
+                final String statusMessage = intent.getStringExtra(EXTRA_STATUS_MESSAGE);
+                switch (status) {
+                    case STATUS_SUCCESS:
+                        break;
+                    case STATUS_FAILURE_NAME_NOT_FOUND:
+                        throw new PackageManager.NameNotFoundException(statusMessage);
+                    case STATUS_FAILURE_ILLEGAL_STATE:
+                        throw new IllegalStateException(statusMessage);
+                    case STATUS_FAILURE_SECURITY:
+                        throw new SecurityException(statusMessage);
+                    default:
+                        throw new RuntimeException(statusMessage);
+                }
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            } catch (ExecutionException e) {
+                throw new RuntimeException(e);
             }
         }
 
diff --git a/core/java/android/hardware/camera2/legacy/RequestQueue.java b/core/java/android/hardware/camera2/legacy/RequestQueue.java
index 407e5e6..fb44402 100644
--- a/core/java/android/hardware/camera2/legacy/RequestQueue.java
+++ b/core/java/android/hardware/camera2/legacy/RequestQueue.java
@@ -30,7 +30,7 @@
 public class RequestQueue {
     private static final String TAG = "RequestQueue";
 
-    private static final long INVALID_FRAME = -1;
+    public static final long INVALID_FRAME = -1;
 
     private BurstHolder mRepeatingRequest = null;
     private final ArrayDeque<BurstHolder> mRequestQueue = new ArrayDeque<BurstHolder>();
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index 32411fb..f9a5029 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -948,8 +948,13 @@
                             Log.d(TAG, "Stopped repeating request. Last frame number is " +
                                     lastFrameNumber);
                         }
-                        mDeviceState.setRepeatingRequestError(lastFrameNumber,
-                                burstHolder.getRequestId());
+                        if (lastFrameNumber != RequestQueue.INVALID_FRAME) {
+                            mDeviceState.setRepeatingRequestError(lastFrameNumber,
+                                    burstHolder.getRequestId());
+                        } else {
+                            Log.e(TAG, "Repeating request id: " + burstHolder.getRequestId() +
+                                    " already canceled!");
+                        }
                     }
 
                     if (DEBUG) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 194068c..d95da91 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1006,7 +1006,7 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+    @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
     @Nullable
     public Network getActiveNetworkForUid(int uid) {
         return getActiveNetworkForUid(uid, false);
@@ -1135,7 +1135,7 @@
      *
      * {@hide}
      */
-    @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+    @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
     @UnsupportedAppUsage
     public NetworkInfo getActiveNetworkInfoForUid(int uid) {
         return getActiveNetworkInfoForUid(uid, false);
@@ -1370,10 +1370,14 @@
      * The system network validation may be using different strategies to detect captive portals,
      * so this method does not necessarily return a URL used by the system. It only returns a URL
      * that may be relevant for other components trying to detect captive portals.
+     *
      * @hide
+     * @deprecated This API returns URL which is not guaranteed to be one of the URLs used by the
+     *             system.
      */
+    @Deprecated
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.LOCAL_MAC_ADDRESS)
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public String getCaptivePortalServerUrl() {
         try {
             return mService.getCaptivePortalServerUrl();
@@ -2399,6 +2403,7 @@
      * @return an array of 0 or more {@code String} of tethered dhcp ranges.
      * {@hide}
      */
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public String[] getTetheredDhcpRanges() {
         try {
             return mService.getTetheredDhcpRanges();
@@ -2978,7 +2983,7 @@
      *        HTTP proxy.  A {@code null} value will clear the global HTTP proxy.
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+    @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
     public void setGlobalProxy(ProxyInfo p) {
         try {
             mService.setGlobalProxy(p);
@@ -3123,6 +3128,7 @@
      * Get the mobile provisioning url.
      * {@hide}
      */
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public String getMobileProvisioningUrl() {
         try {
             return mService.getMobileProvisioningUrl();
@@ -3169,6 +3175,7 @@
 
     /** {@hide} - returns the factory serial number */
     @UnsupportedAppUsage
+    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
     public int registerNetworkFactory(Messenger messenger, String name) {
         try {
             return mService.registerNetworkFactory(messenger, name);
@@ -3179,6 +3186,7 @@
 
     /** {@hide} */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
     public void unregisterNetworkFactory(Messenger messenger) {
         try {
             mService.unregisterNetworkFactory(messenger);
@@ -3196,6 +3204,7 @@
      * Register a NetworkAgent with ConnectivityService.
      * @return NetID corresponding to NetworkAgent.
      */
+    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
     public int registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
             NetworkCapabilities nc, int score, NetworkMisc misc) {
         return registerNetworkAgent(messenger, ni, lp, nc, score, misc,
@@ -3207,6 +3216,7 @@
      * Register a NetworkAgent with ConnectivityService.
      * @return NetID corresponding to NetworkAgent.
      */
+    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
     public int registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
             NetworkCapabilities nc, int score, NetworkMisc misc, int factorySerialNumber) {
         try {
@@ -4201,7 +4211,7 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public void startCaptivePortalApp(Network network) {
         try {
             mService.startCaptivePortalApp(network);
@@ -4317,6 +4327,7 @@
      * Resets all connectivity manager settings back to factory defaults.
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public void factoryReset() {
         try {
             mService.factoryReset();
diff --git a/core/java/android/os/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..fc80e00 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);
     }
 
     /**
@@ -1878,7 +1909,9 @@
      *
      * @param layer            The root layer to capture.
      * @param sourceCrop       The portion of the root surface to capture; caller may pass in 'new
-     *                         Rect()' or null if no cropping is desired.
+     *                         Rect()' or null if no cropping is desired. If the root layer does not
+     *                         have a buffer or a crop set, then a non-empty source crop must be
+     *                         specified.
      * @param frameScale       The desired scale of the returned buffer; the raw
      *                         screen will be scaled up/down.
      *
@@ -1895,7 +1928,9 @@
      *
      * @param layer            The root layer to capture.
      * @param sourceCrop       The portion of the root surface to capture; caller may pass in 'new
-     *                         Rect()' or null if no cropping is desired.
+     *                         Rect()' or null if no cropping is desired. If the root layer does not
+     *                         have a buffer or a crop set, then a non-empty source crop must be
+     *                         specified.
      * @param frameScale       The desired scale of the returned buffer; the raw
      *                         screen will be scaled up/down.
      * @param format           The desired pixel format of the returned buffer.
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 66bf982..f2f84cd 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -529,6 +529,20 @@
     public static final String ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN =
             "android.view.accessibility.action.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN";
 
+    /**
+     * Argument to represent the duration in milliseconds to press and hold a node.
+     * <p>
+     * <strong>Type:</strong> int<br>
+     * <strong>Actions:</strong>
+     * <ul>
+     *     <li>{@link AccessibilityAction#ACTION_PRESS_AND_HOLD}</li>
+     * </ul>
+     *
+     * @see AccessibilityAction#ACTION_PRESS_AND_HOLD
+     */
+    public static final String ACTION_ARGUMENT_PRESS_HOLD_DURATION_MILLIS_INT =
+            "android.view.accessibility.action.ARGUMENT_PRESS_HOLD_DURATION_MILLIS_INT";
+
     // Focus types
 
     /**
@@ -4035,6 +4049,8 @@
                 return "ACTION_SHOW_TOOLTIP";
             case R.id.accessibilityActionHideTooltip:
                 return "ACTION_HIDE_TOOLTIP";
+            case R.id.accessibilityActionPressAndHold:
+                return "ACTION_PRESS_AND_HOLD";
             default:
                 return "ACTION_UNKNOWN";
         }
@@ -4626,6 +4642,31 @@
         public static final AccessibilityAction ACTION_HIDE_TOOLTIP =
                 new AccessibilityAction(R.id.accessibilityActionHideTooltip);
 
+        /**
+         * Action that presses and holds a node.
+         * <p>
+         * This action is for nodes that have distinct behavior that depends on how long a press is
+         * held. Nodes having a single action for long press should use {@link #ACTION_LONG_CLICK}
+         *  instead of this action, and nodes should not expose both actions.
+         * <p>
+         * Use {@link #ACTION_ARGUMENT_PRESS_HOLD_DURATION_MILLIS_INT} to specify how long the
+         * node is pressed. To ensure reasonable behavior, the first value of this argument should
+         * be 0 and the others should greater than 0 and less than 10,000. UIs requested to hold for
+         * times outside of this range should ignore the action.
+         * <p>
+         * The total time the element is held could be specified by an accessibility user up-front,
+         * or may depend on what happens on the UI as the user continues to request the hold.
+         * <p>
+         *   <strong>Note:</strong> The time between dispatching the action and it arriving in the
+         *     UI process is not guaranteed. It is possible on a busy system for the time to expire
+         *     unexpectedly. For the case of holding down a key for a repeating action, a delayed
+         *     arrival should be benign. Please do not use this sort of action in cases where such
+         *     delays will lead to unexpected UI behavior.
+         * <p>
+         */
+        @NonNull public static final AccessibilityAction ACTION_PRESS_AND_HOLD =
+                new AccessibilityAction(R.id.accessibilityActionPressAndHold);
+
         private final int mActionId;
         private final CharSequence mLabel;
 
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 58e80c7..9e23c28 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -136,13 +136,6 @@
      */
     public static final String HASH_SALT_MAX_DAYS = "hash_salt_max_days";
 
-    // Flag related to Privacy Indicators
-
-    /**
-     * Whether the Permissions Hub is showing.
-     */
-    public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
-
     // Flags related to Assistant
 
     /**
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/coretests/src/android/app/timezonedetector/ManualTimeZoneSuggestionTest.java b/core/tests/coretests/src/android/app/timezonedetector/ManualTimeZoneSuggestionTest.java
new file mode 100644
index 0000000..02ed0ed
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timezonedetector/ManualTimeZoneSuggestionTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timezonedetector;
+
+import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
+import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import org.junit.Test;
+
+public class ManualTimeZoneSuggestionTest {
+
+    private static final String ARBITRARY_ZONE_ID1 = "Europe/London";
+    private static final String ARBITRARY_ZONE_ID2 = "Europe/Paris";
+
+    @Test
+    public void testEquals() {
+        ManualTimeZoneSuggestion one = new ManualTimeZoneSuggestion(ARBITRARY_ZONE_ID1);
+        assertEquals(one, one);
+
+        ManualTimeZoneSuggestion two = new ManualTimeZoneSuggestion(ARBITRARY_ZONE_ID1);
+        assertEquals(one, two);
+        assertEquals(two, one);
+
+        ManualTimeZoneSuggestion three = new ManualTimeZoneSuggestion(ARBITRARY_ZONE_ID2);
+        assertNotEquals(one, three);
+        assertNotEquals(three, one);
+
+        // DebugInfo must not be considered in equals().
+        one.addDebugInfo("Debug info 1");
+        two.addDebugInfo("Debug info 2");
+        assertEquals(one, two);
+    }
+
+    @Test
+    public void testParcelable() {
+        ManualTimeZoneSuggestion suggestion = new ManualTimeZoneSuggestion(ARBITRARY_ZONE_ID1);
+        assertRoundTripParcelable(suggestion);
+
+        // DebugInfo should also be stored (but is not checked by equals()
+        suggestion.addDebugInfo("This is debug info");
+        ManualTimeZoneSuggestion rtSuggestion = roundTripParcelable(suggestion);
+        assertEquals(suggestion.getDebugInfo(), rtSuggestion.getDebugInfo());
+    }
+}
diff --git a/core/tests/coretests/src/android/app/timezonedetector/ParcelableTestSupport.java b/core/tests/coretests/src/android/app/timezonedetector/ParcelableTestSupport.java
new file mode 100644
index 0000000..0073d86
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timezonedetector/ParcelableTestSupport.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timezonedetector;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.reflect.Field;
+
+/** Utility methods related to {@link Parcelable} objects used in several tests. */
+public final class ParcelableTestSupport {
+
+    private ParcelableTestSupport() {}
+
+    /** Returns the result of parceling and unparceling the argument. */
+    @SuppressWarnings("unchecked")
+    public static <T extends Parcelable> T roundTripParcelable(T parcelable) {
+        Parcel parcel = Parcel.obtain();
+        parcel.writeTypedObject(parcelable, 0);
+        parcel.setDataPosition(0);
+
+        Parcelable.Creator<T> creator;
+        try {
+            Field creatorField = parcelable.getClass().getField("CREATOR");
+            creator = (Parcelable.Creator<T>) creatorField.get(null);
+        } catch (NoSuchFieldException | IllegalAccessException e) {
+            throw new AssertionError(e);
+        }
+        T toReturn = parcel.readTypedObject(creator);
+        parcel.recycle();
+        return toReturn;
+    }
+
+    public static <T extends Parcelable> void assertRoundTripParcelable(T instance) {
+        assertEquals(instance, roundTripParcelable(instance));
+    }
+}
diff --git a/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java b/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java
index ae91edc..0108a0b 100644
--- a/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java
+++ b/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java
@@ -16,13 +16,13 @@
 
 package android.app.timezonedetector;
 
+import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
+import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
-import android.os.Parcel;
-import android.os.Parcelable;
-
 import org.junit.Test;
 
 public class PhoneTimeZoneSuggestionTest {
@@ -152,19 +152,4 @@
         assertEquals(suggestion1, suggestion1_2);
         assertTrue(suggestion1_2.getDebugInfo().contains(debugString));
     }
-
-    private static void assertRoundTripParcelable(PhoneTimeZoneSuggestion instance) {
-        assertEquals(instance, roundTripParcelable(instance));
-    }
-
-    @SuppressWarnings("unchecked")
-    private static <T extends Parcelable> T roundTripParcelable(T one) {
-        Parcel parcel = Parcel.obtain();
-        parcel.writeTypedObject(one, 0);
-        parcel.setDataPosition(0);
-
-        T toReturn = (T) parcel.readTypedObject(PhoneTimeZoneSuggestion.CREATOR);
-        parcel.recycle();
-        return toReturn;
-    }
 }
diff --git a/core/tests/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/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml
index ee79653..f68d034 100644
--- a/packages/CarSystemUI/res/values/dimens.xml
+++ b/packages/CarSystemUI/res/values/dimens.xml
@@ -60,21 +60,6 @@
     <dimen name="car_keyline_2">96dp</dimen>
     <dimen name="car_keyline_3">128dp</dimen>
 
-    <!-- Height of icons in Ongoing App Ops dialog. Both App Op icon and application icon -->
-    <dimen name="ongoing_appops_dialog_icon_height">48dp</dimen>
-    <!-- Margin between text lines in Ongoing App Ops dialog -->
-    <dimen name="ongoing_appops_dialog_text_margin">15dp</dimen>
-    <!-- Padding around Ongoing App Ops dialog content -->
-    <dimen name="ongoing_appops_dialog_content_padding">24dp</dimen>
-    <!-- Margins around the Ongoing App Ops chip. In landscape, the side margins are 0 -->
-    <dimen name="ongoing_appops_chip_margin">12dp</dimen>
-    <!-- Start and End padding for Ongoing App Ops chip -->
-    <dimen name="ongoing_appops_chip_side_padding">6dp</dimen>
-    <!-- Padding between background of Ongoing App Ops chip and content -->
-    <dimen name="ongoing_appops_chip_bg_padding">4dp</dimen>
-    <!-- Radius of Ongoing App Ops chip corners -->
-    <dimen name="ongoing_appops_chip_bg_corner_radius">12dp</dimen>
-
     <!-- Car volume dimens. -->
     <dimen name="car_volume_item_icon_size">@dimen/car_primary_icon_size</dimen>
     <dimen name="car_volume_item_height">@*android:dimen/car_single_line_list_item_height</dimen>
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..c9ac765 100644
--- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -90,6 +90,8 @@
     private final KeyguardManager mKeyguard;
     private final int mNormalTimeout;
     private final int mHoveringTimeout;
+    private final int mExpNormalTimeout;
+    private final int mExpHoveringTimeout;
 
     private Window mWindow;
     private CustomDialog mDialog;
@@ -176,6 +178,10 @@
                 R.integer.car_volume_dialog_display_normal_timeout);
         mHoveringTimeout = mContext.getResources().getInteger(
                 R.integer.car_volume_dialog_display_hovering_timeout);
+        mExpNormalTimeout = mContext.getResources().getInteger(
+                R.integer.car_volume_dialog_display_expanded_normal_timeout);
+        mExpHoveringTimeout = mContext.getResources().getInteger(
+                R.integer.car_volume_dialog_display_expanded_hovering_timeout);
     }
 
     /** Sets a {@link CarServiceProvider} which connects to the audio service. */
@@ -290,7 +296,7 @@
         mShowing = true;
         clearAllAndSetupDefaultCarVolumeLineItem(mCurrentlyDisplayingGroupId);
         mDialog.show();
-        Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
+        Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
     }
 
     private void clearAllAndSetupDefaultCarVolumeLineItem(int groupId) {
@@ -313,7 +319,11 @@
     }
 
     private int computeTimeoutH() {
-        return mHovering ? mHoveringTimeout : mNormalTimeout;
+        if (mExpanded) {
+            return mHovering ? mExpHoveringTimeout : mExpNormalTimeout;
+        } else {
+            return mHovering ? mHoveringTimeout : mNormalTimeout;
+        }
     }
 
     private void dismissH(int reason) {
@@ -349,7 +359,7 @@
                 }, DISMISS_DELAY_IN_MILLIS))
                 .start();
 
-        Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
+        Events.writeEvent(Events.EVENT_DISMISS_DIALOG, reason);
     }
 
     private void loadAudioUsageItems() {
@@ -532,6 +542,7 @@
         public void onClick(final View v) {
             mExpandIcon = v;
             toggleDialogExpansion(true);
+            rescheduleTimeoutH();
         }
     }
 
diff --git a/packages/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/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
deleted file mode 100644
index b7b21fa..0000000
--- a/packages/SystemUI/res/drawable/privacy_chip_bg.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="#242424" /> <!-- 14% of white -->
-    <padding android:paddingTop="@dimen/ongoing_appops_chip_bg_padding"
-        android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding" />
-    <corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" />
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
deleted file mode 100644
index dce9ce1..0000000
--- a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-
-<com.android.systemui.privacy.OngoingPrivacyChip
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/privacy_chip"
-    android:layout_height="match_parent"
-    android:layout_width="wrap_content"
-    android:layout_gravity="center_vertical|end"
-    android:gravity="center_vertical"
-    android:orientation="horizontal"
-    android:focusable="true" >
-
-        <FrameLayout
-            android:id="@+id/background"
-            android:layout_height="@dimen/ongoing_appops_chip_height"
-            android:minWidth="48dp"
-            android:layout_width="wrap_content" >
-                <LinearLayout
-                    android:id="@+id/icons_container"
-                    android:layout_height="match_parent"
-                    android:layout_width="wrap_content"
-                    android:gravity="center_vertical"
-                    />
-          </FrameLayout>
-</com.android.systemui.privacy.OngoingPrivacyChip>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_text_item.xml b/packages/SystemUI/res/layout/ongoing_privacy_text_item.xml
deleted file mode 100644
index 5595b13..0000000
--- a/packages/SystemUI/res/layout/ongoing_privacy_text_item.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<TextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:textDirection="locale"
-    android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary"
-/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
index 09f512f..7cce1ba 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
@@ -52,7 +52,7 @@
             android:scrollIndicators="top"
             android:scrollbars="vertical"
             android:scrollbarStyle="outsideOverlay"
-            android:importantForAccessibility="no" />
+            android:importantForAccessibility="auto" />
     </com.android.keyguard.AlphaOptimizedLinearLayout>
 
     <View
diff --git a/packages/SystemUI/res/layout/qs_detail_header.xml b/packages/SystemUI/res/layout/qs_detail_header.xml
index a1f0ee7..da80633 100644
--- a/packages/SystemUI/res/layout/qs_detail_header.xml
+++ b/packages/SystemUI/res/layout/qs_detail_header.xml
@@ -53,12 +53,12 @@
             android:src="@drawable/ic_settings"
             android:visibility="gone"/>
 
-        <Switch
-            android:id="@android:id/toggle"
+        <ViewStub
+            android:id="@+id/toggle_stub"
+            android:inflatedId="@+id/toggle"
+            android:layout="@layout/qs_detail_switch"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:clickable="false"
-            android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
+            android:layout_height="wrap_content"/>
 
     </LinearLayout>
 
diff --git a/packages/SystemUI/res/layout/qs_detail_switch.xml b/packages/SystemUI/res/layout/qs_detail_switch.xml
new file mode 100644
index 0000000..abb2497
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_detail_switch.xml
@@ -0,0 +1,23 @@
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<Switch
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/toggle"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:clickable="false"
+    android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_media_panel_options.xml b/packages/SystemUI/res/layout/qs_media_panel_options.xml
new file mode 100644
index 0000000..0669357
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_media_panel_options.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/qs_media_controls_options"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:gravity="center"
+    android:layout_gravity="center"
+    android:padding="10dp"
+    >
+    <ImageButton
+        android:id="@+id/remove"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:src="@android:drawable/ic_menu_delete"
+        android:padding="8dp"
+    />
+    <ImageButton
+        android:id="@+id/cancel"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:src="@android:drawable/ic_menu_revert"
+        android:padding="8dp"
+    />
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index cd9f780..8676767 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -14,7 +14,7 @@
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 -->
-<LinearLayout
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res-auto"
     android:id="@+id/quick_status_bar_system_icons"
@@ -28,13 +28,6 @@
     android:paddingStart="@dimen/status_bar_padding_start"
     android:paddingEnd="@dimen/status_bar_padding_end" >
 
-    <LinearLayout
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:orientation="horizontal"
-        android:gravity="center_vertical|start" >
-
     <com.android.systemui.statusbar.policy.Clock
         android:id="@+id/clock"
         android:layout_width="wrap_content"
@@ -46,23 +39,5 @@
         android:singleLine="true"
         android:textAppearance="@style/TextAppearance.StatusBar.Clock"
         systemui:showDark="false" />
-    </LinearLayout>
 
-    <android.widget.Space
-        android:id="@+id/space"
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_gravity="center_vertical|center_horizontal"
-        android:visibility="gone" />
-
-    <LinearLayout
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:orientation="horizontal"
-        android:gravity="center_vertical|end" >
-
-    <include layout="@layout/ongoing_privacy_chip" />
-
-    </LinearLayout>
-</LinearLayout>
+</FrameLayout>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 076cd22..e896c16 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -462,8 +462,6 @@
         <item>com.android.systemui</item>
     </string-array>
 
-    <integer name="ongoing_appops_dialog_max_apps">5</integer>
-
     <!-- Launcher package name for overlaying icons. -->
     <string name="launcher_overlayable_package" translatable="false">com.android.launcher3</string>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index f7b92b5..c948116 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1067,47 +1067,6 @@
 
     <!-- How much into a DisplayCutout's bounds we can go, on each side -->
     <dimen name="display_cutout_margin_consumption">0px</dimen>
-
-    <!-- Padding below Ongoing App Ops dialog title -->
-    <dimen name="ongoing_appops_dialog_sep">16dp</dimen>
-    <!--Padding around text items in Ongoing App Ops dialog -->
-    <dimen name="ongoing_appops_dialog_text_padding">16dp</dimen>
-    <!-- Height and width of App Opp icons in Ongoing App Ops dialog -->
-    <dimen name="ongoing_appops_dialog_icon_size">24dp</dimen>
-    <!-- Left margin of App Opp icons in Ongoing App Ops dialog -->
-    <dimen name="ongoing_appops_dialog_icon_margin">12dp</dimen>
-    <!-- Height and width of Application icons in Ongoing App Ops dialog -->
-    <dimen name="ongoing_appops_dialog_app_icon_size">32dp</dimen>
-    <!-- Height and width of Plus sign in Ongoing App Ops dialog -->
-    <dimen name="ongoing_appops_dialog_app_plus_size">24dp</dimen>
-    <!-- Height of line in Ongoing App Ops dialog-->
-    <dimen name="ongoing_appops_dialog_line_height">48dp</dimen>
-    <!-- Side margin of title in Ongoing App Ops dialog -->
-    <dimen name="ongoing_appops_dialog_title_margin_sides">24dp</dimen>
-    <!-- Bottom margin of items in Ongoing App Ops dialog -->
-    <dimen name="ongoing_appops_dialog_items_bottom_margin">24dp</dimen>
-    <!-- Top and bottom margin of title in Ongoing App Ops dialog -->
-    <dimen name="ongoing_appops_dialog_title_margin_top_bottom">18dp</dimen>
-    <!-- Text size for Ongoing App Ops dialog title -->
-    <dimen name="ongoing_appops_dialog_title_size">20sp</dimen>
-    <!-- Text size for Ongoing App Ops dialog items -->
-    <dimen name="ongoing_appops_dialog_item_size">16sp</dimen>
-    <!-- Height of the Ongoing App Ops chip -->
-    <dimen name="ongoing_appops_chip_height">32dp</dimen>
-    <!-- Padding between background of Ongoing App Ops chip and content -->
-    <dimen name="ongoing_appops_chip_bg_padding">8dp</dimen>
-    <!-- Side padding between background of Ongoing App Ops chip and content -->
-    <dimen name="ongoing_appops_chip_side_padding">8dp</dimen>
-    <!-- Margin between icons of Ongoing App Ops chip when QQS-->
-    <dimen name="ongoing_appops_chip_icon_margin_collapsed">0dp</dimen>
-    <!-- Margin between icons of Ongoing App Ops chip when QS-->
-    <dimen name="ongoing_appops_chip_icon_margin_expanded">2dp</dimen>
-    <!-- Icon size of Ongoing App Ops chip -->
-    <dimen name="ongoing_appops_chip_icon_size">@dimen/status_bar_icon_drawing_size</dimen>
-    <!-- Radius of Ongoing App Ops chip corners -->
-    <dimen name="ongoing_appops_chip_bg_corner_radius">16dp</dimen>
-
-
     <!-- How much each bubble is elevated. -->
     <dimen name="bubble_elevation">1dp</dimen>
     <!-- How much the bubble flyout text container is elevated. -->
@@ -1179,4 +1138,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/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 99da058..1dd5496 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2444,27 +2444,6 @@
          app for debugging. Will not be seen by users. [CHAR LIMIT=20] -->
     <string name="heap_dump_tile_name">Dump SysUI Heap</string>
 
-    <!-- Content description for ongoing privacy chip. Use with a single app [CHAR LIMIT=NONE]-->
-    <string name="ongoing_privacy_chip_content_single_app"><xliff:g id="app" example="Example App">%1$s</xliff:g> is using your <xliff:g id="types_list" example="camera, location">%2$s</xliff:g>.</string>
-
-    <!-- Content description for ongoing privacy chip. Use with multiple apps [CHAR LIMIT=NONE]-->
-    <string name="ongoing_privacy_chip_content_multiple_apps">Applications are using your <xliff:g id="types_list" example="camera, location">%s</xliff:g>.</string>
-
-    <!-- Separator for types. Include spaces before and after if needed [CHAR LIMIT=10] -->
-    <string name="ongoing_privacy_dialog_separator">,\u0020</string>
-
-    <!-- Separator for types, before last type. Include spaces before and after if needed [CHAR LIMIT=10] -->
-    <string name="ongoing_privacy_dialog_last_separator">\u0020and\u0020</string>
-
-    <!-- Text for camera app op [CHAR LIMIT=20]-->
-    <string name="privacy_type_camera">camera</string>
-
-    <!-- Text for location app op [CHAR LIMIT=20]-->
-    <string name="privacy_type_location">location</string>
-
-    <!-- Text for microphone app op [CHAR LIMIT=20]-->
-    <string name="privacy_type_microphone">microphone</string>
-
     <!-- Text for the quick setting tile for sensor privacy [CHAR LIMIT=30] -->
     <string name="sensor_privacy_mode">Sensors off</string>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 96fbcbb..926d016 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -227,18 +227,6 @@
         <item name="android:textColor">@color/dark_mode_qs_icon_color_single_tone</item>
     </style>
 
-    <style name="TextAppearance.AppOpsDialog" />
-
-    <style name="TextAppearance.AppOpsDialog.Title">
-        <item name="android:textSize">@dimen/ongoing_appops_dialog_title_size</item>
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
-    </style>
-
-    <style name="TextAppearance.AppOpsDialog.Item">
-        <item name="android:textSize">@dimen/ongoing_appops_dialog_item_size</item>
-        <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
-    </style>
-
     <style name="TextAppearance.DeviceManagementDialog">
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 1d4b9ef..9bba2aa 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -151,7 +151,6 @@
     private static final int MSG_DPM_STATE_CHANGED = 309;
     private static final int MSG_USER_SWITCHING = 310;
     private static final int MSG_KEYGUARD_RESET = 312;
-    private static final int MSG_BOOT_COMPLETED = 313;
     private static final int MSG_USER_SWITCH_COMPLETE = 314;
     private static final int MSG_USER_INFO_CHANGED = 317;
     private static final int MSG_REPORT_EMERGENCY_CALL_ACTION = 318;
@@ -234,7 +233,6 @@
     private boolean mGoingToSleep;
     private boolean mBouncer;
     private boolean mAuthInterruptActive;
-    private boolean mBootCompleted;
     private boolean mNeedsSlowUnlockTransition;
     private boolean mHasLockscreenWallpaper;
     private boolean mAssistantVisible;
@@ -1075,8 +1073,6 @@
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state));
             } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
                 mHandler.sendEmptyMessage(MSG_AIRPLANE_MODE_CHANGED);
-            } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
-                dispatchBootCompleted();
             } else if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
                 ServiceState serviceState = ServiceState.newFromBundle(intent.getExtras());
                 int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
@@ -1550,9 +1546,6 @@
                     case MSG_KEYGUARD_BOUNCER_CHANGED:
                         handleKeyguardBouncerChanged(msg.arg1);
                         break;
-                    case MSG_BOOT_COMPLETED:
-                        handleBootCompleted();
-                        break;
                     case MSG_USER_INFO_CHANGED:
                         handleUserInfoChanged(msg.arg1);
                         break;
@@ -1648,11 +1641,6 @@
         filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
         broadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, mHandler);
 
-        final IntentFilter bootCompleteFilter = new IntentFilter();
-        bootCompleteFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        bootCompleteFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
-        broadcastDispatcher.registerReceiver(mBroadcastReceiver, bootCompleteFilter, mHandler);
-
         final IntentFilter allUserFilter = new IntentFilter();
         allUserFilter.addAction(Intent.ACTION_USER_INFO_CHANGED);
         allUserFilter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
@@ -2103,39 +2091,6 @@
     }
 
     /**
-     * This is exposed since {@link Intent#ACTION_BOOT_COMPLETED} is not sticky. If
-     * keyguard crashes sometime after boot, then it will never receive this
-     * broadcast and hence not handle the event. This method is ultimately called by
-     * PhoneWindowManager in this case.
-     */
-    public void dispatchBootCompleted() {
-        mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED);
-    }
-
-    /**
-     * Handle {@link #MSG_BOOT_COMPLETED}
-     */
-    private void handleBootCompleted() {
-        checkIsHandlerThread();
-        if (mBootCompleted) return;
-        mBootCompleted = true;
-        for (int i = 0; i < mCallbacks.size(); i++) {
-            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
-            if (cb != null) {
-                cb.onBootCompleted();
-            }
-        }
-    }
-
-    /**
-     * We need to store this state in the KeyguardUpdateMonitor since this class will not be
-     * destroyed.
-     */
-    public boolean hasBootCompleted() {
-        return mBootCompleted;
-    }
-
-    /**
      * Handle {@link #MSG_DEVICE_PROVISIONED}
      */
     private void handleDeviceProvisioned() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index b4b83d6..04502f0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -148,14 +148,6 @@
     public void onUserUnlocked() { }
 
     /**
-     * Called when boot completed.
-     *
-     * Note, this callback will only be received if boot complete occurs after registering with
-     * KeyguardUpdateMonitor.
-     */
-    public void onBootCompleted() { }
-
-    /**
      * Called when the emergency call button is pressed.
      */
     public void onEmergencyCallAction() { }
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index c55b0d9..6821265 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -56,7 +56,6 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.EnhancedEstimates;
 import com.android.systemui.power.PowerUI;
-import com.android.systemui.privacy.PrivacyItemController;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.shared.plugins.PluginManager;
@@ -298,7 +297,6 @@
     @Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
     @Inject Lazy<AutoHideController> mAutoHideController;
     @Inject Lazy<ForegroundServiceNotificationListener> mForegroundServiceNotificationListener;
-    @Inject Lazy<PrivacyItemController> mPrivacyItemController;
     @Inject @BgLooper Lazy<Looper> mBgLooper;
     @Inject @BgHandler Lazy<Handler> mBgHandler;
     @Inject @MainLooper Lazy<Looper> mMainLooper;
@@ -497,7 +495,6 @@
         mProviders.put(ForegroundServiceNotificationListener.class,
                 mForegroundServiceNotificationListener::get);
         mProviders.put(ClockManager.class, mClockManager::get);
-        mProviders.put(PrivacyItemController.class, mPrivacyItemController::get);
         mProviders.put(ActivityManagerWrapper.class, mActivityManagerWrapper::get);
         mProviders.put(DevicePolicyManagerWrapper.class, mDevicePolicyManagerWrapper::get);
         mProviders.put(PackageManagerWrapper.class, mPackageManagerWrapper::get);
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
index 9793d72..8e49d58 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
@@ -33,6 +33,7 @@
 import androidx.slice.Clock;
 
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
+import com.android.systemui.BootCompleteCache;
 import com.android.systemui.assist.AssistHandleBehaviorController.BehaviorController;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -83,7 +84,6 @@
 
     private static final String[] DEFAULT_HOME_CHANGE_ACTIONS = new String[] {
             PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED,
-            Intent.ACTION_BOOT_COMPLETED,
             Intent.ACTION_PACKAGE_ADDED,
             Intent.ACTION_PACKAGE_CHANGED,
             Intent.ACTION_PACKAGE_REMOVED
@@ -150,6 +150,15 @@
             mDefaultHome = getCurrentDefaultHome();
         }
     };
+
+    private final BootCompleteCache.BootCompleteListener mBootCompleteListener =
+            new BootCompleteCache.BootCompleteListener() {
+        @Override
+        public void onBootComplete() {
+            mDefaultHome = getCurrentDefaultHome();
+        }
+    };
+
     private final IntentFilter mDefaultHomeIntentFilter;
     private final Runnable mResetConsecutiveTaskSwitches = this::resetConsecutiveTaskSwitches;
 
@@ -163,6 +172,7 @@
     private final Lazy<WakefulnessLifecycle> mWakefulnessLifecycle;
     private final Lazy<PackageManagerWrapper> mPackageManagerWrapper;
     private final Lazy<BroadcastDispatcher> mBroadcastDispatcher;
+    private final Lazy<BootCompleteCache> mBootCompleteCache;
 
     private boolean mOnLockscreen;
     private boolean mIsDozing;
@@ -196,7 +206,8 @@
             Lazy<SysUiState> sysUiFlagContainer,
             Lazy<WakefulnessLifecycle> wakefulnessLifecycle,
             Lazy<PackageManagerWrapper> packageManagerWrapper,
-            Lazy<BroadcastDispatcher> broadcastDispatcher) {
+            Lazy<BroadcastDispatcher> broadcastDispatcher,
+            Lazy<BootCompleteCache> bootCompleteCache) {
         mClock = clock;
         mHandler = handler;
         mDeviceConfigHelper = deviceConfigHelper;
@@ -211,6 +222,7 @@
             mDefaultHomeIntentFilter.addAction(action);
         }
         mBroadcastDispatcher = broadcastDispatcher;
+        mBootCompleteCache = bootCompleteCache;
     }
 
     @Override
@@ -218,6 +230,7 @@
         mContext = context;
         mAssistHandleCallbacks = callbacks;
         mConsecutiveTaskSwitches = 0;
+        mBootCompleteCache.get().addListener(mBootCompleteListener);
         mDefaultHome = getCurrentDefaultHome();
         mBroadcastDispatcher.get()
                 .registerReceiver(mDefaultHomeBroadcastReceiver, mDefaultHomeIntentFilter);
@@ -250,6 +263,7 @@
         mAssistHandleCallbacks = null;
         if (mContext != null) {
             mBroadcastDispatcher.get().unregisterReceiver(mDefaultHomeBroadcastReceiver);
+            mBootCompleteCache.get().removeListener(mBootCompleteListener);
             Settings.Secure.putLong(mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, 0);
             Settings.Secure.putInt(mContext.getContentResolver(), LEARNING_EVENT_COUNT_KEY, 0);
             Settings.Secure.putLong(mContext.getContentResolver(), LEARNED_HINT_LAST_SHOWN_KEY, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
index 8cccffa..9de6854 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
@@ -27,6 +27,7 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.systemui.BootCompleteCache;
 import com.android.systemui.Dependency;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -62,7 +63,6 @@
 
     private static final String[] DEFAULT_HOME_CHANGE_ACTIONS = new String[] {
             PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED,
-            Intent.ACTION_BOOT_COMPLETED,
             Intent.ACTION_PACKAGE_ADDED,
             Intent.ACTION_PACKAGE_CHANGED,
             Intent.ACTION_PACKAGE_REMOVED
@@ -77,13 +77,14 @@
 
     @Inject
     PhoneStateMonitor(Context context, BroadcastDispatcher broadcastDispatcher,
-            Optional<Lazy<StatusBar>> statusBarOptionalLazy) {
+            Optional<Lazy<StatusBar>> statusBarOptionalLazy, BootCompleteCache bootCompleteCache) {
         mContext = context;
         mStatusBarOptionalLazy = statusBarOptionalLazy;
         mStatusBarStateController = Dependency.get(StatusBarStateController.class);
 
         ActivityManagerWrapper activityManagerWrapper = ActivityManagerWrapper.getInstance();
         mDefaultHome = getCurrentDefaultHome();
+        bootCompleteCache.addListener(() -> mDefaultHome = getCurrentDefaultHome());
         IntentFilter intentFilter = new IntentFilter();
         for (String action : DEFAULT_HOME_CHANGE_ACTIONS) {
             intentFilter.addAction(action);
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index 776189b..adb288a 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -56,7 +56,7 @@
  * a given broadcast.
  *
  * Use only for IntentFilters with actions and optionally categories. It does not support,
- * permissions, schemes, data types or data authorities.
+ * permissions, schemes, data types, data authorities or priority different than 0.
  * Cannot be used for getting sticky broadcasts.
  */
 @Singleton
@@ -104,6 +104,7 @@
         if (filter.countDataPaths() != 0) sb.append("Filter cannot contain DataPaths. ")
         if (filter.countDataSchemes() != 0) sb.append("Filter cannot contain DataSchemes. ")
         if (filter.countDataTypes() != 0) sb.append("Filter cannot contain DataTypes. ")
+        if (filter.priority != 0) sb.append("Filter cannot modify priority. ")
         if (!TextUtils.isEmpty(sb)) throw IllegalArgumentException(sb.toString())
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index f026e68..8d08b28 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2082,7 +2082,6 @@
 
     @Override
     public void onBootCompleted() {
-        mUpdateMonitor.dispatchBootCompleted();
         synchronized (this) {
             mBootCompleted = true;
             if (mBootSendUserPresent) {
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 4982dd4..eb19571 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -476,7 +476,7 @@
                 });
         d.setOnDismissListener(dialogInterface -> {
             mUsbHighTempDialog = null;
-            Events.writeEvent(mContext, Events.EVENT_DISMISS_USB_OVERHEAT_ALARM,
+            Events.writeEvent(Events.EVENT_DISMISS_USB_OVERHEAT_ALARM,
                     Events.DISMISS_REASON_USB_OVERHEAD_ALARM_CHANGED,
                     mKeyguard.isKeyguardLocked());
         });
@@ -485,7 +485,7 @@
         d.show();
         mUsbHighTempDialog = d;
 
-        Events.writeEvent(mContext, Events.EVENT_SHOW_USB_OVERHEAT_ALARM,
+        Events.writeEvent(Events.EVENT_SHOW_USB_OVERHEAT_ALARM,
                 Events.SHOW_REASON_USB_OVERHEAD_ALARM_CHANGED,
                 mKeyguard.isKeyguardLocked());
     }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
deleted file mode 100644
index a5a915b..0000000
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.privacy
-
-import android.content.Context
-import android.util.AttributeSet
-import android.view.Gravity
-import android.view.ViewGroup
-import android.widget.FrameLayout
-import android.widget.ImageView
-import android.widget.LinearLayout
-import com.android.systemui.R
-
-class OngoingPrivacyChip @JvmOverloads constructor(
-    context: Context,
-    attrs: AttributeSet? = null,
-    defStyleAttrs: Int = 0,
-    defStyleRes: Int = 0
-) : LinearLayout(context, attrs, defStyleAttrs, defStyleRes) {
-
-    private val iconMarginExpanded = context.resources.getDimensionPixelSize(
-                    R.dimen.ongoing_appops_chip_icon_margin_expanded)
-    private val iconMarginCollapsed = context.resources.getDimensionPixelSize(
-                    R.dimen.ongoing_appops_chip_icon_margin_collapsed)
-    private val iconSize =
-            context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size)
-    private val iconColor = context.resources.getColor(
-            R.color.status_bar_clock_color, context.theme)
-    private val sidePadding =
-            context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding)
-    private val backgroundDrawable = context.getDrawable(R.drawable.privacy_chip_bg)
-    private lateinit var iconsContainer: LinearLayout
-    private lateinit var back: FrameLayout
-    var expanded = false
-        set(value) {
-            if (value != field) {
-                field = value
-                updateView()
-            }
-        }
-
-    var builder = PrivacyDialogBuilder(context, emptyList<PrivacyItem>())
-    var privacyList = emptyList<PrivacyItem>()
-        set(value) {
-            field = value
-            builder = PrivacyDialogBuilder(context, value)
-            updateView()
-        }
-
-    override fun onFinishInflate() {
-        super.onFinishInflate()
-
-        back = findViewById(R.id.background)
-        iconsContainer = findViewById(R.id.icons_container)
-    }
-
-    // Should only be called if the builder icons or app changed
-    private fun updateView() {
-        back.background = if (expanded) backgroundDrawable else null
-        val padding = if (expanded) sidePadding else 0
-        back.setPaddingRelative(padding, 0, padding, 0)
-        fun setIcons(dialogBuilder: PrivacyDialogBuilder, iconsContainer: ViewGroup) {
-            iconsContainer.removeAllViews()
-            dialogBuilder.generateIcons().forEachIndexed { i, it ->
-                it.mutate()
-                it.setTint(iconColor)
-                val image = ImageView(context).apply {
-                    setImageDrawable(it)
-                    scaleType = ImageView.ScaleType.CENTER_INSIDE
-                }
-                iconsContainer.addView(image, iconSize, iconSize)
-                if (i != 0) {
-                    val lp = image.layoutParams as MarginLayoutParams
-                    lp.marginStart = if (expanded) iconMarginExpanded else iconMarginCollapsed
-                    image.layoutParams = lp
-                }
-            }
-        }
-
-        if (!privacyList.isEmpty()) {
-            generateContentDescription()
-            setIcons(builder, iconsContainer)
-            val lp = iconsContainer.layoutParams as FrameLayout.LayoutParams
-            lp.gravity = Gravity.CENTER_VERTICAL or
-                    (if (expanded) Gravity.CENTER_HORIZONTAL else Gravity.END)
-            iconsContainer.layoutParams = lp
-        } else {
-            iconsContainer.removeAllViews()
-        }
-        requestLayout()
-    }
-
-    private fun generateContentDescription() {
-        val typesText = builder.joinTypes()
-        contentDescription = context.getString(
-                R.string.ongoing_privacy_chip_content_multiple_apps, typesText)
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
deleted file mode 100644
index d08a373..0000000
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.privacy
-
-import android.content.Context
-import android.graphics.drawable.Drawable
-import com.android.systemui.R
-
-class PrivacyDialogBuilder(private val context: Context, itemsList: List<PrivacyItem>) {
-
-    val appsAndTypes: List<Pair<PrivacyApplication, List<PrivacyType>>>
-    val types: List<PrivacyType>
-    private val separator = context.getString(R.string.ongoing_privacy_dialog_separator)
-    private val lastSeparator = context.getString(R.string.ongoing_privacy_dialog_last_separator)
-
-    init {
-        appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType })
-                .toList()
-                .sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps
-                        { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest)
-        types = itemsList.map { it.privacyType }.distinct().sorted()
-    }
-
-    fun generateIconsForApp(types: List<PrivacyType>): List<Drawable> {
-        return types.sorted().map { it.getIcon(context) }
-    }
-
-    fun generateIcons() = types.map { it.getIcon(context) }
-
-    private fun <T> List<T>.joinWithAnd(): StringBuilder {
-        return subList(0, size - 1).joinTo(StringBuilder(), separator = separator).apply {
-            append(lastSeparator)
-            append(this@joinWithAnd.last())
-        }
-    }
-
-    fun joinTypes(): String {
-        return when (types.size) {
-            0 -> ""
-            1 -> types[0].getName(context)
-            else -> types.map { it.getName(context) }.joinWithAnd().toString()
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
deleted file mode 100644
index 2909424..0000000
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.privacy
-
-import android.content.Context
-import android.content.pm.ApplicationInfo
-import android.content.pm.PackageManager
-import android.graphics.drawable.Drawable
-import android.os.UserHandle
-import android.util.IconDrawableFactory
-import com.android.systemui.R
-
-typealias Privacy = PrivacyType
-
-enum class PrivacyType(private val nameId: Int, val iconId: Int) {
-    // This is uses the icons used by the corresponding permission groups in the AndroidManifest
-    TYPE_CAMERA(R.string.privacy_type_camera,
-            com.android.internal.R.drawable.perm_group_camera),
-    TYPE_MICROPHONE(R.string.privacy_type_microphone,
-            com.android.internal.R.drawable.perm_group_microphone),
-    TYPE_LOCATION(R.string.privacy_type_location,
-            com.android.internal.R.drawable.perm_group_location);
-
-    fun getName(context: Context) = context.resources.getString(nameId)
-
-    fun getIcon(context: Context) = context.resources.getDrawable(iconId, context.theme)
-}
-
-data class PrivacyItem(
-    val privacyType: PrivacyType,
-    val application: PrivacyApplication
-)
-
-data class PrivacyApplication(val packageName: String, val uid: Int, val context: Context)
-    : Comparable<PrivacyApplication> {
-
-    override fun compareTo(other: PrivacyApplication): Int {
-        return applicationName.compareTo(other.applicationName)
-    }
-
-    private val applicationInfo: ApplicationInfo? by lazy {
-        try {
-            val userHandle = UserHandle.getUserHandleForUid(uid)
-            context.createPackageContextAsUser(packageName, 0, userHandle).getPackageManager()
-                    .getApplicationInfo(packageName, 0)
-        } catch (_: PackageManager.NameNotFoundException) {
-            null
-        }
-    }
-    val icon: Drawable by lazy {
-        applicationInfo?.let {
-            try {
-                val iconFactory = IconDrawableFactory.newInstance(context, true)
-                iconFactory.getBadgedIcon(it, UserHandle.getUserId(uid))
-            } catch (_: Exception) {
-                null
-            }
-        } ?: context.getDrawable(android.R.drawable.sym_def_app_icon)
-    }
-
-    val applicationName: String by lazy {
-        applicationInfo?.let {
-            context.packageManager.getApplicationLabel(it) as String
-        } ?: packageName
-    }
-
-    override fun toString() = "PrivacyApplication(packageName=$packageName, uid=$uid)"
-}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
deleted file mode 100644
index d592492..0000000
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.privacy
-
-import android.app.ActivityManager
-import android.app.AppOpsManager
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import android.os.Handler
-import android.os.Looper
-import android.os.Message
-import android.os.UserHandle
-import android.os.UserManager
-import android.provider.DeviceConfig
-import com.android.internal.annotations.VisibleForTesting
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
-import com.android.systemui.Dumpable
-import com.android.systemui.R
-import com.android.systemui.appops.AppOpItem
-import com.android.systemui.appops.AppOpsController
-import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.dagger.qualifiers.BgHandler
-import com.android.systemui.dagger.qualifiers.MainHandler
-import java.io.FileDescriptor
-import java.io.PrintWriter
-import java.lang.ref.WeakReference
-import javax.inject.Inject
-import javax.inject.Singleton
-
-fun isPermissionsHubEnabled() = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
-
-@Singleton
-class PrivacyItemController @Inject constructor(
-    private val context: Context,
-    private val appOpsController: AppOpsController,
-    @MainHandler private val uiHandler: Handler,
-    @BgHandler private val bgHandler: Handler,
-    private val broadcastDispatcher: BroadcastDispatcher
-) : Dumpable {
-
-    @VisibleForTesting
-    internal companion object {
-        val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
-                AppOpsManager.OP_RECORD_AUDIO,
-                AppOpsManager.OP_COARSE_LOCATION,
-                AppOpsManager.OP_FINE_LOCATION)
-        val intents = listOf(Intent.ACTION_USER_FOREGROUND,
-                Intent.ACTION_MANAGED_PROFILE_ADDED,
-                Intent.ACTION_MANAGED_PROFILE_REMOVED)
-        const val TAG = "PrivacyItemController"
-        const val SYSTEM_UID = 1000
-        const val MSG_ADD_CALLBACK = 0
-        const val MSG_REMOVE_CALLBACK = 1
-        const val MSG_UPDATE_LISTENING_STATE = 2
-    }
-
-    @VisibleForTesting
-    internal var privacyList = emptyList<PrivacyItem>()
-        @Synchronized get() = field.toList() // Returns a shallow copy of the list
-        @Synchronized set
-
-    private val userManager = context.getSystemService(UserManager::class.java)
-    private var currentUserIds = emptyList<Int>()
-    private var listening = false
-    val systemApp =
-            PrivacyApplication(context.getString(R.string.device_services), SYSTEM_UID, context)
-    private val callbacks = mutableListOf<WeakReference<Callback>>()
-    private val messageHandler = H(WeakReference(this), uiHandler.looper)
-
-    private val notifyChanges = Runnable {
-        val list = privacyList
-        callbacks.forEach { it.get()?.privacyChanged(list) }
-    }
-
-    private val updateListAndNotifyChanges = Runnable {
-        updatePrivacyList()
-        uiHandler.post(notifyChanges)
-    }
-
-    private var indicatorsAvailable = isPermissionsHubEnabled()
-    @VisibleForTesting
-    internal val devicePropertiesChangedListener =
-            object : DeviceConfig.OnPropertiesChangedListener {
-        override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
-            if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) &&
-                    properties.getKeyset().contains(
-                    SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED)) {
-                indicatorsAvailable = properties.getBoolean(
-                        SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
-                messageHandler.removeMessages(MSG_UPDATE_LISTENING_STATE)
-                messageHandler.sendEmptyMessage(MSG_UPDATE_LISTENING_STATE)
-            }
-        }
-    }
-
-    private val cb = object : AppOpsController.Callback {
-        override fun onActiveStateChanged(
-            code: Int,
-            uid: Int,
-            packageName: String,
-            active: Boolean
-        ) {
-            val userId = UserHandle.getUserId(uid)
-            if (userId in currentUserIds) {
-                update(false)
-            }
-        }
-    }
-
-    @VisibleForTesting
-    internal var userSwitcherReceiver = Receiver()
-        set(value) {
-            context.unregisterReceiver(field)
-            field = value
-            registerReceiver()
-        }
-
-    init {
-        DeviceConfig.addOnPropertiesChangedListener(
-                DeviceConfig.NAMESPACE_PRIVACY,
-                context.mainExecutor,
-                devicePropertiesChangedListener)
-    }
-
-    private fun unregisterReceiver() {
-        broadcastDispatcher.unregisterReceiver(userSwitcherReceiver)
-    }
-
-    private fun registerReceiver() {
-        broadcastDispatcher.registerReceiver(userSwitcherReceiver, IntentFilter().apply {
-            intents.forEach {
-                addAction(it)
-            }
-        }, null /* handler */, UserHandle.ALL)
-    }
-
-    private fun update(updateUsers: Boolean) {
-        if (updateUsers) {
-            val currentUser = ActivityManager.getCurrentUser()
-            currentUserIds = userManager.getProfiles(currentUser).map { it.id }
-        }
-        bgHandler.post(updateListAndNotifyChanges)
-    }
-
-    /**
-     * Updates listening status based on whether there are callbacks and the indicators are enabled
-     *
-     * This is only called from private (add/remove)Callback and from the config listener, all in
-     * main thread.
-     */
-    private fun setListeningState() {
-        val listen = !callbacks.isEmpty() and indicatorsAvailable
-        if (listening == listen) return
-        listening = listen
-        if (listening) {
-            appOpsController.addCallback(OPS, cb)
-            registerReceiver()
-            update(true)
-        } else {
-            appOpsController.removeCallback(OPS, cb)
-            unregisterReceiver()
-            // Make sure that we remove all indicators and notify listeners if we are not
-            // listening anymore due to indicators being disabled
-            update(false)
-        }
-    }
-
-    private fun addCallback(callback: WeakReference<Callback>) {
-        callbacks.add(callback)
-        if (callbacks.isNotEmpty() && !listening) {
-            messageHandler.removeMessages(MSG_UPDATE_LISTENING_STATE)
-            messageHandler.sendEmptyMessage(MSG_UPDATE_LISTENING_STATE)
-        }
-        // Notify this callback if we didn't set to listening
-        else if (listening) uiHandler.post(NotifyChangesToCallback(callback.get(), privacyList))
-    }
-
-    private fun removeCallback(callback: WeakReference<Callback>) {
-        // Removes also if the callback is null
-        callbacks.removeIf { it.get()?.equals(callback.get()) ?: true }
-        if (callbacks.isEmpty()) {
-            messageHandler.removeMessages(MSG_UPDATE_LISTENING_STATE)
-            messageHandler.sendEmptyMessage(MSG_UPDATE_LISTENING_STATE)
-        }
-    }
-
-    fun addCallback(callback: Callback) {
-        messageHandler.obtainMessage(MSG_ADD_CALLBACK, callback).sendToTarget()
-    }
-
-    fun removeCallback(callback: Callback) {
-        messageHandler.obtainMessage(MSG_REMOVE_CALLBACK, callback).sendToTarget()
-    }
-
-    private fun updatePrivacyList() {
-        if (!listening) {
-            privacyList = emptyList()
-            return
-        }
-        val list = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
-                .mapNotNull { toPrivacyItem(it) }.distinct()
-        privacyList = list
-    }
-
-    private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
-        val type: PrivacyType = when (appOpItem.code) {
-            AppOpsManager.OP_CAMERA -> PrivacyType.TYPE_CAMERA
-            AppOpsManager.OP_COARSE_LOCATION -> PrivacyType.TYPE_LOCATION
-            AppOpsManager.OP_FINE_LOCATION -> PrivacyType.TYPE_LOCATION
-            AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
-            else -> return null
-        }
-        if (appOpItem.uid == SYSTEM_UID) return PrivacyItem(type, systemApp)
-        val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid, context)
-        return PrivacyItem(type, app)
-    }
-
-    // Used by containing class to get notified of changes
-    interface Callback {
-        fun privacyChanged(privacyItems: List<PrivacyItem>)
-    }
-
-    internal inner class Receiver : BroadcastReceiver() {
-        override fun onReceive(context: Context?, intent: Intent?) {
-            if (intent?.action in intents) {
-                update(true)
-            }
-        }
-    }
-
-    private class NotifyChangesToCallback(
-        private val callback: Callback?,
-        private val list: List<PrivacyItem>
-    ) : Runnable {
-        override fun run() {
-            callback?.privacyChanged(list)
-        }
-    }
-
-    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
-        pw.println("PrivacyItemController state:")
-        pw.println("  Listening: $listening")
-        pw.println("  Current user ids: $currentUserIds")
-        pw.println("  Privacy Items:")
-        privacyList.forEach {
-            pw.print("    ")
-            pw.println(it.toString())
-        }
-        pw.println("  Callbacks:")
-        callbacks.forEach {
-            it.get()?.let {
-                pw.print("    ")
-                pw.println(it.toString())
-            }
-        }
-    }
-
-    private class H(
-        private val outerClass: WeakReference<PrivacyItemController>,
-        looper: Looper
-    ) : Handler(looper) {
-        override fun handleMessage(msg: Message) {
-            super.handleMessage(msg)
-            when (msg.what) {
-                MSG_UPDATE_LISTENING_STATE -> outerClass.get()?.setListeningState()
-
-                MSG_ADD_CALLBACK -> {
-                    if (msg.obj !is PrivacyItemController.Callback) return
-                    outerClass.get()?.addCallback(
-                            WeakReference(msg.obj as PrivacyItemController.Callback))
-                }
-
-                MSG_REMOVE_CALLBACK -> {
-                    if (msg.obj !is PrivacyItemController.Callback) return
-                    outerClass.get()?.removeCallback(
-                            WeakReference(msg.obj as PrivacyItemController.Callback))
-                }
-                else -> {}
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 60d30da..019cb14 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -28,6 +28,7 @@
 import android.util.SparseArray;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewStub;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -58,7 +59,8 @@
 
     protected View mQsDetailHeader;
     protected TextView mQsDetailHeaderTitle;
-    protected Switch mQsDetailHeaderSwitch;
+    private ViewStub mQsDetailHeaderSwitchStub;
+    private Switch mQsDetailHeaderSwitch;
     protected ImageView mQsDetailHeaderProgress;
 
     protected QSTileHost mHost;
@@ -98,7 +100,7 @@
 
         mQsDetailHeader = findViewById(R.id.qs_detail_header);
         mQsDetailHeaderTitle = (TextView) mQsDetailHeader.findViewById(android.R.id.title);
-        mQsDetailHeaderSwitch = (Switch) mQsDetailHeader.findViewById(android.R.id.toggle);
+        mQsDetailHeaderSwitchStub = mQsDetailHeader.findViewById(R.id.toggle_stub);
         mQsDetailHeaderProgress = findViewById(R.id.qs_detail_header_progress);
 
         updateDetailText();
@@ -252,9 +254,12 @@
         mQsDetailHeaderTitle.setText(adapter.getTitle());
         final Boolean toggleState = adapter.getToggleState();
         if (toggleState == null) {
-            mQsDetailHeaderSwitch.setVisibility(INVISIBLE);
+            if (mQsDetailHeaderSwitch != null) mQsDetailHeaderSwitch.setVisibility(INVISIBLE);
             mQsDetailHeader.setClickable(false);
         } else {
+            if (mQsDetailHeaderSwitch == null) {
+                mQsDetailHeaderSwitch = (Switch) mQsDetailHeaderSwitchStub.inflate();
+            }
             mQsDetailHeaderSwitch.setVisibility(VISIBLE);
             handleToggleStateChanged(toggleState, adapter.getToggleEnabled());
             mQsDetailHeader.setClickable(true);
@@ -274,9 +279,9 @@
         if (mAnimatingOpen) {
             return;
         }
-        mQsDetailHeaderSwitch.setChecked(state);
+        if (mQsDetailHeaderSwitch != null) mQsDetailHeaderSwitch.setChecked(state);
         mQsDetailHeader.setEnabled(toggleEnabled);
-        mQsDetailHeaderSwitch.setEnabled(toggleEnabled);
+        if (mQsDetailHeaderSwitch != null) mQsDetailHeaderSwitch.setEnabled(toggleEnabled);
     }
 
     private void handleScanStateChanged(boolean state) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
index 1a4c327..f7e4c79 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
@@ -18,8 +18,11 @@
 
 import android.app.Notification;
 import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.content.res.ColorStateList;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -35,6 +38,7 @@
 import android.media.session.PlaybackState;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -54,6 +58,8 @@
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
 
+import java.util.List;
+
 /**
  * Single media player for carousel in QSPanel
  */
@@ -70,6 +76,83 @@
     private int mHeight;
     private int mForegroundColor;
     private int mBackgroundColor;
+    private ComponentName mRecvComponent;
+    private QSPanel mParent;
+
+    private MediaController.Callback mSessionCallback = new MediaController.Callback() {
+        @Override
+        public void onSessionDestroyed() {
+            Log.d(TAG, "session destroyed");
+            mController.unregisterCallback(mSessionCallback);
+
+            // Hide all the old buttons
+            final int[] actionIds = {
+                    R.id.action0,
+                    R.id.action1,
+                    R.id.action2,
+                    R.id.action3,
+                    R.id.action4
+            };
+            for (int i = 0; i < actionIds.length; i++) {
+                ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]);
+                if (thisBtn != null) {
+                    thisBtn.setVisibility(View.GONE);
+                }
+            }
+
+            // Add a restart button
+            ImageButton btn = mMediaNotifView.findViewById(actionIds[0]);
+            btn.setOnClickListener(v -> {
+                Log.d(TAG, "Attempting to restart session");
+                // Send a media button event to previously found receiver
+                if (mRecvComponent != null) {
+                    Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+                    intent.setComponent(mRecvComponent);
+                    int keyCode = KeyEvent.KEYCODE_MEDIA_PLAY;
+                    intent.putExtra(
+                            Intent.EXTRA_KEY_EVENT,
+                            new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
+                    mContext.sendBroadcast(intent);
+                } else {
+                    Log.d(TAG, "No receiver to restart");
+                    // If we don't have a receiver, try relaunching the activity instead
+                    try {
+                        mController.getSessionActivity().send();
+                    } catch (PendingIntent.CanceledException e) {
+                        Log.e(TAG, "Pending intent was canceled");
+                        e.printStackTrace();
+                    }
+                }
+            });
+            btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_replay));
+            btn.setImageTintList(ColorStateList.valueOf(mForegroundColor));
+            btn.setVisibility(View.VISIBLE);
+
+            // Add long-click option to remove the player
+            ViewGroup mMediaCarousel = (ViewGroup) mMediaNotifView.getParent();
+            mMediaNotifView.setOnLongClickListener(v -> {
+                // Replace player view with delete/cancel view
+                v.setVisibility(View.GONE);
+
+                View options = LayoutInflater.from(mContext).inflate(
+                        R.layout.qs_media_panel_options, null, false);
+                ImageButton btnDelete = options.findViewById(R.id.remove);
+                btnDelete.setOnClickListener(b -> {
+                    mMediaCarousel.removeView(options);
+                    mParent.removeMediaPlayer(QSMediaPlayer.this);
+                });
+                ImageButton btnCancel = options.findViewById(R.id.cancel);
+                btnCancel.setOnClickListener(b -> {
+                    mMediaCarousel.removeView(options);
+                    v.setVisibility(View.VISIBLE);
+                });
+
+                int pos = mMediaCarousel.indexOfChild(v);
+                mMediaCarousel.addView(options, pos, v.getLayoutParams());
+                return true; // consumed click
+            });
+        }
+    };
 
     /**
      *
@@ -92,7 +175,8 @@
     }
 
     /**
-     *
+     * Create or update the player view for the given media session
+     * @param parent the parent QSPanel
      * @param token token for this media session
      * @param icon app notification icon
      * @param iconColor foreground color (for text, icons)
@@ -101,13 +185,30 @@
      * @param notif reference to original notification
      * @param device current playback device
      */
-    public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor,
-            View actionsContainer, Notification notif, MediaDevice device) {
-        Log.d(TAG, "got media session: " + token);
+    public void setMediaSession(QSPanel parent, MediaSession.Token token, Icon icon, int iconColor,
+            int bgColor, View actionsContainer, Notification notif, MediaDevice device) {
+        mParent = parent;
         mToken = token;
         mForegroundColor = iconColor;
         mBackgroundColor = bgColor;
         mController = new MediaController(mContext, token);
+
+        // Try to find a receiver for the media button that matches this app
+        PackageManager pm = mContext.getPackageManager();
+        Intent it = new Intent(Intent.ACTION_MEDIA_BUTTON);
+        List<ResolveInfo> info = pm.queryBroadcastReceiversAsUser(it, 0, mContext.getUser());
+        if (info != null) {
+            for (ResolveInfo inf : info) {
+                if (inf.activityInfo.packageName.equals(notif.contentIntent.getCreatorPackage())) {
+                    Log.d(TAG, "Found receiver for package: " + inf);
+                    mRecvComponent = inf.getComponentInfo().getComponentName();
+                }
+            }
+        }
+
+        // reset in case we had previously restarted the stream
+        mMediaNotifView.setOnLongClickListener(null);
+        mController.registerCallback(mSessionCallback);
         MediaMetadata mMediaMetadata = mController.getMetadata();
         if (mMediaMetadata == null) {
             Log.e(TAG, "Media metadata was null");
@@ -235,7 +336,6 @@
         for (; i < actionIds.length; i++) {
             ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]);
             thisBtn.setVisibility(View.GONE);
-            Log.d(TAG, "hid a button");
         }
     }
 
@@ -266,8 +366,9 @@
 
     private void addAlbumArtBackground(MediaMetadata metadata, int bgColor, int width, int height) {
         Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
+        float radius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
         if (albumArt != null) {
-
+            Log.d(TAG, "updating album art");
             Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true);
             Bitmap scaled = scaleBitmap(original, width, height);
             Canvas canvas = new Canvas(scaled);
@@ -281,12 +382,15 @@
 
             RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(
                     mContext.getResources(), scaled);
-            roundedDrawable.setCornerRadius(20);
+            roundedDrawable.setCornerRadius(radius);
 
             mMediaNotifView.setBackground(roundedDrawable);
         } else {
             Log.e(TAG, "No album art available");
-            mMediaNotifView.setBackground(null);
+            GradientDrawable rect = new GradientDrawable();
+            rect.setCornerRadius(radius);
+            rect.setColor(bgColor);
+            mMediaNotifView.setBackground(rect);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 5e98f93..51e352b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -284,7 +284,7 @@
         }
 
         Log.d(TAG, "setting player session");
-        player.setMediaSession(token, icon, iconColor, bgColor, actionsContainer,
+        player.setMediaSession(this, token, icon, iconColor, bgColor, actionsContainer,
                 notif.getNotification(), mDevice);
 
         if (mMediaPlayers.size() > 0) {
@@ -303,6 +303,27 @@
         return mMediaCarousel;
     }
 
+    /**
+     * Remove the media player from the carousel
+     * @param player Player to remove
+     * @return true if removed, false if player was not found
+     */
+    protected boolean removeMediaPlayer(QSMediaPlayer player) {
+        // Remove from list
+        if (!mMediaPlayers.remove(player)) {
+            return false;
+        }
+
+        // Check if we need to collapse the carousel now
+        mMediaCarousel.removeView(player.getView());
+        if (mMediaPlayers.size() == 0) {
+            ((View) mMediaCarousel.getParent()).setVisibility(View.GONE);
+            mLocalMediaManager.stopScan();
+            mLocalMediaManager.unregisterCallback(mDeviceCallback);
+        }
+        return true;
+    }
+
     protected void addDivider() {
         mDivider = LayoutInflater.from(mContext).inflate(R.layout.qs_divider, this, false);
         mDivider.setBackgroundColor(Utils.applyAlpha(mDivider.getAlpha(),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
index d7b8b83..5bb882e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
@@ -16,19 +16,27 @@
 
 package com.android.systemui.qs;
 
+import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.ColorStateList;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.Icon;
 import android.media.MediaMetadata;
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState;
 import android.util.Log;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -42,6 +50,8 @@
 
 import com.android.systemui.R;
 
+import java.util.List;
+
 /**
  * QQS mini media player
  */
@@ -53,6 +63,54 @@
     private LinearLayout mMediaNotifView;
     private MediaSession.Token mToken;
     private MediaController mController;
+    private int mBackgroundColor;
+    private int mForegroundColor;
+    private ComponentName mRecvComponent;
+
+    private MediaController.Callback mSessionCallback = new MediaController.Callback() {
+        @Override
+        public void onSessionDestroyed() {
+            Log.d(TAG, "session destroyed");
+            mController.unregisterCallback(mSessionCallback);
+
+            // Hide all the old buttons
+            final int[] actionIds = {R.id.action0, R.id.action1, R.id.action2};
+            for (int i = 0; i < actionIds.length; i++) {
+                ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]);
+                if (thisBtn != null) {
+                    thisBtn.setVisibility(View.GONE);
+                }
+            }
+
+            // Add a restart button
+            ImageButton btn = mMediaNotifView.findViewById(actionIds[0]);
+            btn.setOnClickListener(v -> {
+                Log.d(TAG, "Attempting to restart session");
+                // Send a media button event to previously found receiver
+                if (mRecvComponent != null) {
+                    Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+                    intent.setComponent(mRecvComponent);
+                    int keyCode = KeyEvent.KEYCODE_MEDIA_PLAY;
+                    intent.putExtra(
+                            Intent.EXTRA_KEY_EVENT,
+                            new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
+                    mContext.sendBroadcast(intent);
+                } else {
+                    Log.d(TAG, "No receiver to restart");
+                    // If we don't have a receiver, try relaunching the activity instead
+                    try {
+                        mController.getSessionActivity().send();
+                    } catch (PendingIntent.CanceledException e) {
+                        Log.e(TAG, "Pending intent was canceled");
+                        e.printStackTrace();
+                    }
+                }
+            });
+            btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_replay));
+            btn.setImageTintList(ColorStateList.valueOf(mForegroundColor));
+            btn.setVisibility(View.VISIBLE);
+        }
+    };
 
     /**
      *
@@ -83,34 +141,50 @@
             View actionsContainer, int[] actionsToShow) {
         Log.d(TAG, "Setting media session: " + token);
         mToken = token;
+        mForegroundColor = iconColor;
+        mBackgroundColor = bgColor;
         mController = new MediaController(mContext, token);
         MediaMetadata mMediaMetadata = mController.getMetadata();
 
+        // Try to find a receiver for the media button that matches this app
+        PackageManager pm = mContext.getPackageManager();
+        Intent it = new Intent(Intent.ACTION_MEDIA_BUTTON);
+        List<ResolveInfo> info = pm.queryBroadcastReceiversAsUser(it, 0, mContext.getUser());
+        if (info != null) {
+            for (ResolveInfo inf : info) {
+                if (inf.activityInfo.packageName.equals(mController.getPackageName())) {
+                    Log.d(TAG, "Found receiver for package: " + inf);
+                    mRecvComponent = inf.getComponentInfo().getComponentName();
+                }
+            }
+        }
+        mController.registerCallback(mSessionCallback);
+
         if (mMediaMetadata == null) {
             Log.e(TAG, "Media metadata was null");
             return;
         }
 
         // Album art
-        addAlbumArtBackground(mMediaMetadata, bgColor);
+        addAlbumArtBackground(mMediaMetadata, mBackgroundColor);
 
         // App icon
         ImageView appIcon = mMediaNotifView.findViewById(R.id.icon);
         Drawable iconDrawable = icon.loadDrawable(mContext);
-        iconDrawable.setTint(iconColor);
+        iconDrawable.setTint(mForegroundColor);
         appIcon.setImageDrawable(iconDrawable);
 
         // Artist name
         TextView appText = mMediaNotifView.findViewById(R.id.header_title);
         String artistName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
         appText.setText(artistName);
-        appText.setTextColor(iconColor);
+        appText.setTextColor(mForegroundColor);
 
         // Song name
         TextView titleText = mMediaNotifView.findViewById(R.id.header_text);
         String songName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
         titleText.setText(songName);
-        titleText.setTextColor(iconColor);
+        titleText.setTextColor(mForegroundColor);
 
         // Buttons we can display
         final int[] actionIds = {R.id.action0, R.id.action1, R.id.action2};
@@ -178,6 +252,7 @@
 
     private void addAlbumArtBackground(MediaMetadata metadata, int bgColor) {
         Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
+        float radius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
         if (albumArt != null) {
             Rect bounds = new Rect();
             mMediaNotifView.getBoundsOnScreen(bounds);
@@ -197,12 +272,15 @@
 
             RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(
                     mContext.getResources(), scaled);
-            roundedDrawable.setCornerRadius(20);
+            roundedDrawable.setCornerRadius(radius);
 
             mMediaNotifView.setBackground(roundedDrawable);
         } else {
             Log.e(TAG, "No album art available");
-            mMediaNotifView.setBackground(null);
+            GradientDrawable rect = new GradientDrawable();
+            rect.setCornerRadius(radius);
+            rect.setColor(bgColor);
+            mMediaNotifView.setBackground(rect);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 02e8f59..e5cec87 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -33,30 +33,24 @@
 import android.graphics.Rect;
 import android.media.AudioManager;
 import android.os.Handler;
-import android.os.Looper;
 import android.provider.AlarmClock;
-import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.service.notification.ZenModeConfig;
 import android.text.format.DateUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Pair;
-import android.util.StatsLog;
 import android.view.ContextThemeWrapper;
 import android.view.DisplayCutout;
 import android.view.View;
 import android.view.WindowInsets;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
-import android.widget.Space;
 import android.widget.TextView;
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.settingslib.Utils;
 import com.android.systemui.BatteryMeterView;
 import com.android.systemui.DualToneHandler;
@@ -65,11 +59,6 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.privacy.OngoingPrivacyChip;
-import com.android.systemui.privacy.PrivacyDialogBuilder;
-import com.android.systemui.privacy.PrivacyItem;
-import com.android.systemui.privacy.PrivacyItemController;
-import com.android.systemui.privacy.PrivacyItemControllerKt;
 import com.android.systemui.qs.QSDetail.Callback;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.PhoneStatusBarView;
@@ -145,12 +134,8 @@
     private View mRingerContainer;
     private Clock mClockView;
     private DateView mDateView;
-    private OngoingPrivacyChip mPrivacyChip;
-    private Space mSpace;
     private BatteryMeterView mBatteryRemainingIcon;
-    private boolean mPermissionsHubEnabled;
 
-    private PrivacyItemController mPrivacyItemController;
     private BroadcastDispatcher mBroadcastDispatcher;
 
     private final BroadcastReceiver mRingerReceiver = new BroadcastReceiver() {
@@ -161,43 +146,18 @@
         }
     };
     private boolean mHasTopCutout = false;
-    private boolean mPrivacyChipLogged = false;
-
-    private final DeviceConfig.OnPropertiesChangedListener mPropertiesListener =
-            new DeviceConfig.OnPropertiesChangedListener() {
-                @Override
-                public void onPropertiesChanged(DeviceConfig.Properties properties) {
-                    if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace())
-                            && properties.getKeyset()
-                            .contains(SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED)) {
-                        mPermissionsHubEnabled = properties.getBoolean(
-                                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false);
-                        StatusIconContainer iconContainer = findViewById(R.id.statusIcons);
-                        iconContainer.setIgnoredSlots(getIgnoredIconSlots());
-                    }
-                }
-            };
-
-    private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
-        @Override
-        public void privacyChanged(List<PrivacyItem> privacyItems) {
-            mPrivacyChip.setPrivacyList(privacyItems);
-            setChipVisibility(!privacyItems.isEmpty());
-        }
-    };
 
     @Inject
     public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
             NextAlarmController nextAlarmController, ZenModeController zenModeController,
             StatusBarIconController statusBarIconController,
-            ActivityStarter activityStarter, PrivacyItemController privacyItemController,
+            ActivityStarter activityStarter,
             CommandQueue commandQueue, BroadcastDispatcher broadcastDispatcher) {
         super(context, attrs);
         mAlarmController = nextAlarmController;
         mZenController = zenModeController;
         mStatusBarIconController = statusBarIconController;
         mActivityStarter = activityStarter;
-        mPrivacyItemController = privacyItemController;
         mDualToneHandler = new DualToneHandler(
                 new ContextThemeWrapper(context, R.style.QSHeaderTheme));
         mBroadcastDispatcher = broadcastDispatcher;
@@ -228,11 +188,8 @@
         mRingerModeTextView = findViewById(R.id.ringer_mode_text);
         mRingerContainer = findViewById(R.id.ringer_container);
         mRingerContainer.setOnClickListener(this::onClick);
-        mPrivacyChip = findViewById(R.id.privacy_chip);
-        mPrivacyChip.setOnClickListener(this::onClick);
         mCarrierGroup = findViewById(R.id.carrier_group);
 
-
         updateResources();
 
         Rect tintArea = new Rect(0, 0, 0, 0);
@@ -252,7 +209,6 @@
         mClockView = findViewById(R.id.clock);
         mClockView.setOnClickListener(this);
         mDateView = findViewById(R.id.date);
-        mSpace = findViewById(R.id.space);
 
         // Tint for the battery icons are handled in setupHost()
         mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
@@ -263,8 +219,6 @@
         mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
         mRingerModeTextView.setSelected(true);
         mNextAlarmTextView.setSelected(true);
-
-        mPermissionsHubEnabled = PrivacyItemControllerKt.isPermissionsHubEnabled();
     }
 
     private List<String> getIgnoredIconSlots() {
@@ -273,10 +227,6 @@
                 com.android.internal.R.string.status_bar_camera));
         ignored.add(mContext.getResources().getString(
                 com.android.internal.R.string.status_bar_microphone));
-        if (mPermissionsHubEnabled) {
-            ignored.add(mContext.getResources().getString(
-                    com.android.internal.R.string.status_bar_location));
-        }
 
         return ignored;
     }
@@ -292,21 +242,6 @@
         }
     }
 
-    private void setChipVisibility(boolean chipVisible) {
-        if (chipVisible && mPermissionsHubEnabled) {
-            mPrivacyChip.setVisibility(View.VISIBLE);
-            // Makes sure that the chip is logged as viewed at most once each time QS is opened
-            // mListening makes sure that the callback didn't return after the user closed QS
-            if (!mPrivacyChipLogged && mListening) {
-                mPrivacyChipLogged = true;
-                StatsLog.write(StatsLog.PRIVACY_INDICATORS_INTERACTED,
-                        StatsLog.PRIVACY_INDICATORS_INTERACTED__TYPE__CHIP_VIEWED);
-            }
-        } else {
-            mPrivacyChip.setVisibility(View.GONE);
-        }
-    }
-
     private boolean updateRingerStatus() {
         boolean isOriginalVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
         CharSequence originalRingerText = mRingerModeTextView.getText();
@@ -418,7 +353,6 @@
 
         updateStatusIconAlphaAnimator();
         updateHeaderTextContainerAlphaAnimator();
-        updatePrivacyChipAlphaAnimator();
     }
 
     private void updateStatusIconAlphaAnimator() {
@@ -433,12 +367,6 @@
                 .build();
     }
 
-    private void updatePrivacyChipAlphaAnimator() {
-        mPrivacyChipAlphaAnimator = new TouchAnimator.Builder()
-                .addFloat(mPrivacyChip, "alpha", 1, 0, 1)
-                .build();
-    }
-
     public void setExpanded(boolean expanded) {
         if (mExpanded == expanded) return;
         mExpanded = expanded;
@@ -477,10 +405,6 @@
                 mHeaderTextContainerView.setVisibility(INVISIBLE);
             }
         }
-        if (mPrivacyChipAlphaAnimator != null) {
-            mPrivacyChip.setExpanded(expansionFraction > 0.5);
-            mPrivacyChipAlphaAnimator.setPosition(keyguardExpansionFraction);
-        }
     }
 
     public void disable(int state1, int state2, boolean animate) {
@@ -498,9 +422,6 @@
         super.onAttachedToWindow();
         mStatusBarIconController.addIconGroup(mIconManager);
         requestApplyInsets();
-        // Change the ignored slots when DeviceConfig flag changes
-        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_PRIVACY,
-                mContext.getMainExecutor(), mPropertiesListener);
     }
 
     @Override
@@ -516,21 +437,6 @@
             mSystemIconsView.setPadding(padding.first, 0, padding.second, 0);
 
         }
-        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpace.getLayoutParams();
-        if (cutout != null) {
-            Rect topCutout = cutout.getBoundingRectTop();
-            if (topCutout.isEmpty()) {
-                mHasTopCutout = false;
-                lp.width = 0;
-                mSpace.setVisibility(View.GONE);
-            } else {
-                mHasTopCutout = true;
-                lp.width = topCutout.width();
-                mSpace.setVisibility(View.VISIBLE);
-            }
-        }
-        mSpace.setLayoutParams(lp);
-        setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
         return super.onApplyWindowInsets(insets);
     }
 
@@ -539,7 +445,6 @@
     public void onDetachedFromWindow() {
         setListening(false);
         mStatusBarIconController.removeIconGroup(mIconManager);
-        DeviceConfig.removeOnPropertiesChangedListener(mPropertiesListener);
         super.onDetachedFromWindow();
     }
 
@@ -555,13 +460,10 @@
             mAlarmController.addCallback(this);
             mBroadcastDispatcher.registerReceiver(mRingerReceiver,
                     new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
-            mPrivacyItemController.addCallback(mPICCallback);
         } else {
             mZenController.removeCallback(this);
             mAlarmController.removeCallback(this);
-            mPrivacyItemController.removeCallback(mPICCallback);
             mBroadcastDispatcher.unregisterReceiver(mRingerReceiver);
-            mPrivacyChipLogged = false;
         }
     }
 
@@ -579,18 +481,6 @@
                 mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
                         AlarmClock.ACTION_SHOW_ALARMS), 0);
             }
-        } else if (v == mPrivacyChip) {
-            // Makes sure that the builder is grabbed as soon as the chip is pressed
-            PrivacyDialogBuilder builder = mPrivacyChip.getBuilder();
-            if (builder.getAppsAndTypes().size() == 0) return;
-            Handler mUiHandler = new Handler(Looper.getMainLooper());
-            StatsLog.write(StatsLog.PRIVACY_INDICATORS_INTERACTED,
-                    StatsLog.PRIVACY_INDICATORS_INTERACTED__TYPE__CHIP_CLICKED);
-            mUiHandler.post(() -> {
-                mActivityStarter.postStartActivityDismissingKeyguard(
-                        new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0);
-                mHost.collapsePanels();
-            });
         } else if (v == mRingerContainer && mRingerContainer.isVisibleToUser()) {
             mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
                     Settings.ACTION_SOUND_SETTINGS), 0);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
index 20e3cee..47cb45b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
@@ -49,4 +49,9 @@
     protected boolean animationsEnabled() {
         return false;
     }
+
+    @Override
+    public boolean isLongClickable() {
+        return false;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index ff34be0..1de6355 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -32,6 +32,7 @@
 import android.widget.Toolbar;
 import android.widget.Toolbar.OnMenuItemClickListener;
 
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 import androidx.recyclerview.widget.DefaultItemAnimator;
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
@@ -118,7 +119,13 @@
         mTileQueryHelper.setListener(mTileAdapter);
         mRecyclerView.setAdapter(mTileAdapter);
         mTileAdapter.getItemTouchHelper().attachToRecyclerView(mRecyclerView);
-        GridLayoutManager layout = new GridLayoutManager(getContext(), 3);
+        GridLayoutManager layout = new GridLayoutManager(getContext(), 3) {
+            @Override
+            public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler,
+                    RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) {
+                // Do not read row and column every time it changes.
+            }
+        };
         layout.setSpanSizeLookup(mTileAdapter.getSizeLookup());
         mRecyclerView.setLayoutManager(layout);
         mRecyclerView.addItemDecoration(mTileAdapter.getItemDecoration());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index bd3297b..3afc460 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -238,9 +238,21 @@
         return true;
     }
 
+    private void setSelectableForHeaders(View view) {
+        if (mAccessibilityManager.isTouchExplorationEnabled()) {
+            final boolean selectable = mAccessibilityAction == ACTION_NONE;
+            view.setFocusable(selectable);
+            view.setImportantForAccessibility(selectable
+                    ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
+                    : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+            view.setFocusableInTouchMode(selectable);
+        }
+    }
+
     @Override
     public void onBindViewHolder(final Holder holder, int position) {
         if (holder.getItemViewType() == TYPE_HEADER) {
+            setSelectableForHeaders(holder.itemView);
             return;
         }
         if (holder.getItemViewType() == TYPE_DIVIDER) {
@@ -260,6 +272,8 @@
             }
 
             ((TextView) holder.itemView.findViewById(android.R.id.title)).setText(titleText);
+            setSelectableForHeaders(holder.itemView);
+
             return;
         }
         if (holder.getItemViewType() == TYPE_ACCESSIBLE_DROP) {
@@ -306,6 +320,7 @@
             holder.mTileView.setImportantForAccessibility(selectable
                     ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
                     : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+            holder.mTileView.setFocusableInTouchMode(selectable);
             if (selectable) {
                 holder.mTileView.setOnClickListener(new OnClickListener() {
                     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index dc9a2ce..9fe9703 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -22,6 +22,7 @@
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
 import android.text.TextUtils;
+import android.widget.Switch;
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.systemui.R;
@@ -112,6 +113,7 @@
             state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
         }
         state.showRippleEffect = false;
+        state.expandedAccessibilityClassName = Switch.class.getName();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 5a5f9e9..9dd7f48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -174,7 +174,7 @@
             if (ops.contains(OP_CAMERA) || ops.contains(OP_RECORD_AUDIO)) {
                 startAppDetailsSettingsActivity(pkg, uid, null, row);
             } else {
-                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
+                Intent intent = new Intent(Settings.ACTION_MANAGE_APP_OVERLAY_PERMISSION);
                 intent.setData(Uri.fromParts("package", pkg, null));
                 mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
index 2a4b315..352ba0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -179,21 +179,22 @@
         if (Utils.useQsMediaPlayer(mContext)) {
             final int[] compactActions = mRow.getEntry().getSbn().getNotification().extras
                     .getIntArray(Notification.EXTRA_COMPACT_ACTIONS);
+            int tintColor = getNotificationHeader().getOriginalIconColor();
             StatusBarWindowController ctrl = Dependency.get(StatusBarWindowController.class);
             QuickQSPanel panel = ctrl.getStatusBarView().findViewById(
                     com.android.systemui.R.id.quick_qs_panel);
             panel.getMediaPlayer().setMediaSession(token,
                     mRow.getEntry().getSbn().getNotification().getSmallIcon(),
-                    getNotificationHeader().getOriginalIconColor(),
-                    mRow.getCurrentBackgroundTint(),
+                    tintColor,
+                    mBackgroundColor,
                     mActions,
                     compactActions);
             QSPanel bigPanel = ctrl.getStatusBarView().findViewById(
                     com.android.systemui.R.id.quick_settings_panel);
             bigPanel.addMediaSession(token,
                     mRow.getEntry().getSbn().getNotification().getSmallIcon(),
-                    getNotificationHeader().getOriginalIconColor(),
-                    mRow.getCurrentBackgroundTint(),
+                    tintColor,
+                    mBackgroundColor,
                     mActions,
                     mRow.getEntry().getSbn());
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index bfecaaa..8d43c66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -43,10 +43,6 @@
 import com.android.systemui.R;
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.privacy.PrivacyItem;
-import com.android.systemui.privacy.PrivacyItemController;
-import com.android.systemui.privacy.PrivacyItemControllerKt;
-import com.android.systemui.privacy.PrivacyType;
 import com.android.systemui.qs.tiles.DndTile;
 import com.android.systemui.qs.tiles.RotationLockTile;
 import com.android.systemui.statusbar.CommandQueue;
@@ -67,9 +63,6 @@
 import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.List;
 import java.util.Locale;
 
 /**
@@ -84,12 +77,12 @@
                 ZenModeController.Callback,
                 DeviceProvisionedListener,
                 KeyguardStateController.Callback,
-                PrivacyItemController.Callback,
                 LocationController.LocationChangeCallback {
     private static final String TAG = "PhoneStatusBarPolicy";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    public static final int LOCATION_STATUS_ICON_ID = PrivacyType.TYPE_LOCATION.getIconId();
+    public static final int LOCATION_STATUS_ICON_ID =
+            com.android.internal.R.drawable.perm_group_location;
 
     private final String mSlotCast;
     private final String mSlotHotspot;
@@ -122,7 +115,6 @@
     private final DeviceProvisionedController mProvisionedController;
     private final KeyguardStateController mKeyguardStateController;
     private final LocationController mLocationController;
-    private final PrivacyItemController mPrivacyItemController;
     private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
     private final SensorPrivacyController mSensorPrivacyController;
 
@@ -156,7 +148,6 @@
         mProvisionedController = Dependency.get(DeviceProvisionedController.class);
         mKeyguardStateController = Dependency.get(KeyguardStateController.class);
         mLocationController = Dependency.get(LocationController.class);
-        mPrivacyItemController = Dependency.get(PrivacyItemController.class);
         mSensorPrivacyController = Dependency.get(SensorPrivacyController.class);
 
         mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
@@ -233,13 +224,6 @@
                 context.getString(R.string.accessibility_data_saver_on));
         mIconController.setIconVisibility(mSlotDataSaver, false);
 
-        // privacy items
-        mIconController.setIcon(mSlotMicrophone, PrivacyType.TYPE_MICROPHONE.getIconId(),
-                PrivacyType.TYPE_MICROPHONE.getName(mContext));
-        mIconController.setIconVisibility(mSlotMicrophone, false);
-        mIconController.setIcon(mSlotCamera, PrivacyType.TYPE_CAMERA.getIconId(),
-                PrivacyType.TYPE_CAMERA.getName(mContext));
-        mIconController.setIconVisibility(mSlotCamera, false);
         mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
                 mContext.getString(R.string.accessibility_location_active));
         mIconController.setIconVisibility(mSlotLocation, false);
@@ -259,7 +243,6 @@
         mNextAlarmController.addCallback(mNextAlarmCallback);
         mDataSaver.addCallback(this);
         mKeyguardStateController.addCallback(this);
-        mPrivacyItemController.addCallback(this);
         mSensorPrivacyController.addCallback(mSensorPrivacyListener);
         mLocationController.addCallback(this);
 
@@ -603,46 +586,9 @@
         mIconController.setIconVisibility(mSlotDataSaver, isDataSaving);
     }
 
-    @Override  // PrivacyItemController.Callback
-    public void privacyChanged(List<PrivacyItem> privacyItems) {
-        updatePrivacyItems(privacyItems);
-    }
-
-    private void updatePrivacyItems(List<PrivacyItem> items) {
-        boolean showCamera = false;
-        boolean showMicrophone = false;
-        boolean showLocation = false;
-        for (PrivacyItem item : items) {
-            if (item == null /* b/124234367 */) {
-                if (DEBUG) {
-                    Log.e(TAG, "updatePrivacyItems - null item found");
-                    StringWriter out = new StringWriter();
-                    mPrivacyItemController.dump(null, new PrintWriter(out), null);
-                    Log.e(TAG, out.toString());
-                }
-                continue;
-            }
-            switch (item.getPrivacyType()) {
-                case TYPE_CAMERA:
-                    showCamera = true;
-                    break;
-                case TYPE_LOCATION:
-                    showLocation = true;
-                    break;
-                case TYPE_MICROPHONE:
-                    showMicrophone = true;
-                    break;
-            }
-        }
-
-        mIconController.setIconVisibility(mSlotCamera, showCamera);
-        mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
-        mIconController.setIconVisibility(mSlotLocation, showLocation);
-    }
-
     @Override
     public void onLocationActiveChanged(boolean active) {
-        if (!PrivacyItemControllerKt.isPermissionsHubEnabled()) updateLocation();
+        updateLocation();
     }
 
     // Updates the status view based on the current state of location requests.
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ConfigurableTexts.java b/packages/SystemUI/src/com/android/systemui/volume/ConfigurableTexts.java
index 20de5bb..f1b9590 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ConfigurableTexts.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ConfigurableTexts.java
@@ -43,6 +43,9 @@
 
     public int add(final TextView text, final int labelResId) {
         if (text == null) return 0;
+        if (mTexts.containsKey(text)) {
+            return mTexts.get(text);
+        }
         final Resources res = mContext.getResources();
         final float fontScale = res.getConfiguration().fontScale;
         final float density = res.getDisplayMetrics().density;
@@ -63,6 +66,11 @@
         return sp;
     }
 
+    public void remove(final TextView text) {
+        mTexts.remove(text);
+        mTextLabels.remove(text);
+    }
+
     public void update() {
         if (mTexts.isEmpty()) return;
         mTexts.keyAt(0).post(mUpdateAll);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java
index 9bbfd22..5ed8b8f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Events.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.volume;
 
-import android.content.Context;
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.provider.Settings.Global;
@@ -125,7 +124,12 @@
 
     public static Callback sCallback;
 
-    public static void writeEvent(Context context, int tag, Object... list) {
+    /**
+     * Logs an event to the system log and the event log.
+     * @param tag One of the EVENT_* codes above.
+     * @param list Any additional event-specific arguments, documented above.
+     */
+    public static void writeEvent(int tag, Object... list) {
         MetricsLogger logger = new MetricsLogger();
         final long time = System.currentTimeMillis();
         final StringBuilder sb = new StringBuilder("writeEvent ").append(EVENT_TAGS[tag]);
@@ -133,33 +137,33 @@
             sb.append(" ");
             switch (tag) {
                 case EVENT_SHOW_DIALOG:
-                    MetricsLogger.visible(context, MetricsEvent.VOLUME_DIALOG);
-                    MetricsLogger.histogram(context, "volume_from_keyguard",
+                    logger.visible(MetricsEvent.VOLUME_DIALOG);
+                    logger.histogram("volume_from_keyguard",
                             (Boolean) list[1] ? 1 : 0);
                     sb.append(SHOW_REASONS[(Integer) list[0]]).append(" keyguard=").append(list[1]);
                     break;
                 case EVENT_EXPAND:
-                    MetricsLogger.visibility(context, MetricsEvent.VOLUME_DIALOG_DETAILS,
+                    logger.visibility(MetricsEvent.VOLUME_DIALOG_DETAILS,
                             (Boolean) list[0]);
                     sb.append(list[0]);
                     break;
                 case EVENT_DISMISS_DIALOG:
-                    MetricsLogger.hidden(context, MetricsEvent.VOLUME_DIALOG);
+                    logger.hidden(MetricsEvent.VOLUME_DIALOG);
                     sb.append(DISMISS_REASONS[(Integer) list[0]]);
                     break;
                 case EVENT_ACTIVE_STREAM_CHANGED:
-                    MetricsLogger.action(context, MetricsEvent.ACTION_VOLUME_STREAM,
+                    logger.action(MetricsEvent.ACTION_VOLUME_STREAM,
                             (Integer) list[0]);
                     sb.append(AudioSystem.streamToString((Integer) list[0]));
                     break;
                 case EVENT_ICON_CLICK:
-                    MetricsLogger.action(context, MetricsEvent.ACTION_VOLUME_ICON,
+                    logger.action(MetricsEvent.ACTION_VOLUME_ICON,
                             (Integer) list[0]);
                     sb.append(AudioSystem.streamToString((Integer) list[0])).append(' ')
                             .append(iconStateToString((Integer) list[1]));
                     break;
                 case EVENT_TOUCH_LEVEL_DONE:
-                    MetricsLogger.action(context, MetricsEvent.ACTION_VOLUME_SLIDER,
+                    logger.action(MetricsEvent.ACTION_VOLUME_SLIDER,
                             (Integer) list[1]);
                     // fall through
                 case EVENT_TOUCH_LEVEL_CHANGED:
@@ -169,7 +173,7 @@
                             .append(list[1]);
                     break;
                 case EVENT_KEY:
-                    MetricsLogger.action(context, MetricsEvent.ACTION_VOLUME_KEY,
+                    logger.action(MetricsEvent.ACTION_VOLUME_KEY,
                             (Integer) list[0]);
                     sb.append(AudioSystem.streamToString((Integer) list[0])).append(' ')
                             .append(list[1]);
@@ -181,7 +185,7 @@
                     logger.action(MetricsEvent.ACTION_VOLUME_SETTINGS);
                     break;
                 case EVENT_EXTERNAL_RINGER_MODE_CHANGED:
-                    MetricsLogger.action(context, MetricsEvent.ACTION_RINGER_MODE,
+                    logger.action(MetricsEvent.ACTION_RINGER_MODE,
                             (Integer) list[0]);
                     // fall through
                 case EVENT_INTERNAL_RINGER_MODE_CHANGED:
@@ -194,14 +198,14 @@
                     sb.append(list[0]).append(' ').append(list[1]);
                     break;
                 case EVENT_SHOW_USB_OVERHEAT_ALARM:
-                    MetricsLogger.visible(context, MetricsEvent.POWER_OVERHEAT_ALARM);
-                    MetricsLogger.histogram(context, "show_usb_overheat_alarm",
+                    logger.visible(MetricsEvent.POWER_OVERHEAT_ALARM);
+                    logger.histogram("show_usb_overheat_alarm",
                             (Boolean) list[1] ? 1 : 0);
                     sb.append(SHOW_REASONS[(Integer) list[0]]).append(" keyguard=").append(list[1]);
                     break;
                 case EVENT_DISMISS_USB_OVERHEAT_ALARM:
-                    MetricsLogger.hidden(context, MetricsEvent.POWER_OVERHEAT_ALARM);
-                    MetricsLogger.histogram(context, "dismiss_usb_overheat_alarm",
+                    logger.hidden(MetricsEvent.POWER_OVERHEAT_ALARM);
+                    logger.histogram("dismiss_usb_overheat_alarm",
                             (Boolean) list[1] ? 1 : 0);
                     sb.append(DISMISS_REASONS[(Integer) list[0]])
                         .append(" keyguard=").append(list[1]);
@@ -255,4 +259,5 @@
         void writeEvent(long time, int tag, Object[] list);
         void writeState(long time, State state);
     }
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 02c699f..a4ed31d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -150,7 +150,7 @@
         mStatusBarOptionalLazy = statusBarOptionalLazy;
         mNotificationManager = (NotificationManager) mContext.getSystemService(
                 Context.NOTIFICATION_SERVICE);
-        Events.writeEvent(mContext, Events.EVENT_COLLECTION_STARTED);
+        Events.writeEvent(Events.EVENT_COLLECTION_STARTED);
         mWorkerThread = new HandlerThread(VolumeDialogControllerImpl.class.getSimpleName());
         mWorkerThread.start();
         mWorker = new W(mWorkerThread.getLooper());
@@ -237,7 +237,7 @@
         if (D.BUG) Log.d(TAG, "destroy");
         if (mDestroyed) return;
         mDestroyed = true;
-        Events.writeEvent(mContext, Events.EVENT_COLLECTION_STOPPED);
+        Events.writeEvent(Events.EVENT_COLLECTION_STOPPED);
         mMediaSessions.destroy();
         mObserver.destroy();
         mReceiver.destroy();
@@ -487,7 +487,7 @@
             mCallbacks.onShowSilentHint();
         }
         if (changed && fromKey) {
-            Events.writeEvent(mContext, Events.EVENT_KEY, stream, lastAudibleStreamVolume);
+            Events.writeEvent(Events.EVENT_KEY, stream, lastAudibleStreamVolume);
         }
         return changed;
     }
@@ -495,7 +495,7 @@
     private boolean updateActiveStreamW(int activeStream) {
         if (activeStream == mState.activeStream) return false;
         mState.activeStream = activeStream;
-        Events.writeEvent(mContext, Events.EVENT_ACTIVE_STREAM_CHANGED, activeStream);
+        Events.writeEvent(Events.EVENT_ACTIVE_STREAM_CHANGED, activeStream);
         if (D.BUG) Log.d(TAG, "updateActiveStreamW " + activeStream);
         final int s = activeStream < DYNAMIC_STREAM_START_INDEX ? activeStream : -1;
         if (D.BUG) Log.d(TAG, "forceVolumeControlStream " + s);
@@ -544,7 +544,7 @@
         if (ss.level == level) return false;
         ss.level = level;
         if (isLogWorthy(stream)) {
-            Events.writeEvent(mContext, Events.EVENT_LEVEL_CHANGED, stream, level);
+            Events.writeEvent(Events.EVENT_LEVEL_CHANGED, stream, level);
         }
         return true;
     }
@@ -567,7 +567,7 @@
         if (ss.muted == muted) return false;
         ss.muted = muted;
         if (isLogWorthy(stream)) {
-            Events.writeEvent(mContext, Events.EVENT_MUTE_CHANGED, stream, muted);
+            Events.writeEvent(Events.EVENT_MUTE_CHANGED, stream, muted);
         }
         if (muted && isRinger(stream)) {
             updateRingerModeInternalW(mAudio.getRingerModeInternal());
@@ -583,7 +583,7 @@
         if (Objects.equals(mState.effectsSuppressor, effectsSuppressor)) return false;
         mState.effectsSuppressor = effectsSuppressor;
         mState.effectsSuppressorName = getApplicationName(mContext, mState.effectsSuppressor);
-        Events.writeEvent(mContext, Events.EVENT_SUPPRESSOR_CHANGED, mState.effectsSuppressor,
+        Events.writeEvent(Events.EVENT_SUPPRESSOR_CHANGED, mState.effectsSuppressor,
                 mState.effectsSuppressorName);
         return true;
     }
@@ -607,7 +607,7 @@
                 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
         if (mState.zenMode == zen) return false;
         mState.zenMode = zen;
-        Events.writeEvent(mContext, Events.EVENT_ZEN_MODE_CHANGED, zen);
+        Events.writeEvent(Events.EVENT_ZEN_MODE_CHANGED, zen);
         return true;
     }
 
@@ -633,23 +633,23 @@
         mState.disallowMedia = disallowMedia;
         mState.disallowSystem = disallowSystem;
         mState.disallowRinger = disallowRinger;
-        Events.writeEvent(mContext, Events.EVENT_ZEN_CONFIG_CHANGED, "disallowAlarms=" +
-                disallowAlarms + " disallowMedia=" + disallowMedia + " disallowSystem=" +
-                disallowSystem + " disallowRinger=" + disallowRinger);
+        Events.writeEvent(Events.EVENT_ZEN_CONFIG_CHANGED, "disallowAlarms="
+                + disallowAlarms + " disallowMedia=" + disallowMedia + " disallowSystem="
+                + disallowSystem + " disallowRinger=" + disallowRinger);
         return true;
     }
 
     private boolean updateRingerModeExternalW(int rm) {
         if (rm == mState.ringerModeExternal) return false;
         mState.ringerModeExternal = rm;
-        Events.writeEvent(mContext, Events.EVENT_EXTERNAL_RINGER_MODE_CHANGED, rm);
+        Events.writeEvent(Events.EVENT_EXTERNAL_RINGER_MODE_CHANGED, rm);
         return true;
     }
 
     private boolean updateRingerModeInternalW(int rm) {
         if (rm == mState.ringerModeInternal) return false;
         mState.ringerModeInternal = rm;
-        Events.writeEvent(mContext, Events.EVENT_INTERNAL_RINGER_MODE_CHANGED, rm);
+        Events.writeEvent(Events.EVENT_INTERNAL_RINGER_MODE_CHANGED, rm);
 
         if (mState.ringerModeInternal == RINGER_MODE_NORMAL) {
             playTouchFeedback();
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 24d6c4c..f5c1587 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -429,7 +429,7 @@
         row.icon.setImageResource(iconRes);
         if (row.stream != AudioSystem.STREAM_ACCESSIBILITY) {
             row.icon.setOnClickListener(v -> {
-                Events.writeEvent(mContext, Events.EVENT_ICON_CLICK, row.stream, row.iconState);
+                Events.writeEvent(Events.EVENT_ICON_CLICK, row.stream, row.iconState);
                 mController.setActiveStream(row.stream);
                 if (row.stream == AudioManager.STREAM_RING) {
                     final boolean hasVibrator = mController.hasVibrator();
@@ -468,7 +468,7 @@
         }
         if (mSettingsIcon != null) {
             mSettingsIcon.setOnClickListener(v -> {
-                Events.writeEvent(mContext, Events.EVENT_SETTINGS_CLICK);
+                Events.writeEvent(Events.EVENT_SETTINGS_CLICK);
                 Intent intent = new Intent(Settings.Panel.ACTION_VOLUME);
                 dismissH(DISMISS_REASON_SETTINGS_CLICKED);
                 Dependency.get(ActivityStarter.class).startActivity(intent,
@@ -504,7 +504,7 @@
                         mController.setStreamVolume(AudioManager.STREAM_RING, 1);
                     }
                 }
-                Events.writeEvent(mContext, Events.EVENT_RINGER_TOGGLE, newRingerMode);
+                Events.writeEvent(Events.EVENT_RINGER_TOGGLE, newRingerMode);
                 incrementManualToggleCount();
                 updateRingerH();
                 provideTouchFeedbackH(newRingerMode);
@@ -519,7 +519,7 @@
         if (mODICaptionsIcon != null) {
             mODICaptionsIcon.setOnConfirmedTapListener(() -> {
                 onCaptionIconClicked();
-                Events.writeEvent(mContext, Events.EVENT_ODI_CAPTIONS_CLICK);
+                Events.writeEvent(Events.EVENT_ODI_CAPTIONS_CLICK);
             }, mHandler);
         }
 
@@ -541,7 +541,7 @@
             mODICaptionsTooltipView = mODICaptionsTooltipViewStub.inflate();
             mODICaptionsTooltipView.findViewById(R.id.dismiss).setOnClickListener(v -> {
                 hideCaptionsTooltip();
-                Events.writeEvent(mContext, Events.EVENT_ODI_CAPTIONS_TOOLTIP_CLICK);
+                Events.writeEvent(Events.EVENT_ODI_CAPTIONS_TOOLTIP_CLICK);
             });
             mODICaptionsTooltipViewStub = null;
             rescheduleTimeoutH();
@@ -694,7 +694,7 @@
         initSettingsH();
         mShowing = true;
         mDialog.show();
-        Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
+        Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
         mController.notifyVisible(true);
         mController.getCaptionsComponentState(false);
         checkODICaptionsTooltip(false);
@@ -741,7 +741,7 @@
         if (mShowing) {
             mShowing = false;
             // Only logs when the volume dialog visibility is changed.
-            Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
+            Events.writeEvent(Events.EVENT_DISMISS_DIALOG, reason);
         }
         mDialogView.setTranslationX(0);
         mDialogView.setAlpha(1);
@@ -922,6 +922,7 @@
             if (!mDynamic.get(row.stream)) {
                 mRows.remove(i);
                 mDialogRowsView.removeView(row.view);
+                mConfigurableTexts.remove(row.header);
             }
         }
     }
@@ -1400,7 +1401,7 @@
                     mController.setActiveStream(mRow.stream);
                     mController.setStreamVolume(mRow.stream, userLevel);
                     mRow.requestedLevel = userLevel;
-                    Events.writeEvent(mContext, Events.EVENT_TOUCH_LEVEL_CHANGED, mRow.stream,
+                    Events.writeEvent(Events.EVENT_TOUCH_LEVEL_CHANGED, mRow.stream,
                             userLevel);
                 }
             }
@@ -1419,7 +1420,7 @@
             mRow.tracking = false;
             mRow.userAttempt = SystemClock.uptimeMillis();
             final int userLevel = getImpliedLevel(seekBar, seekBar.getProgress());
-            Events.writeEvent(mContext, Events.EVENT_TOUCH_LEVEL_DONE, mRow.stream, userLevel);
+            Events.writeEvent(Events.EVENT_TOUCH_LEVEL_DONE, mRow.stream, userLevel);
             if (mRow.ss.level != userLevel) {
                 mHandler.sendMessageDelayed(mHandler.obtainMessage(H.RECHECK, mRow),
                         USER_ATTEMPT_GRACE_PERIOD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
index ead14e5..2242c1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
@@ -178,6 +178,14 @@
         broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
     }
 
+    @Test(expected = IllegalArgumentException::class)
+    fun testFilterMustNotSetPriority() {
+        val testFilter = IntentFilter(TEST_ACTION).apply {
+            priority = IntentFilter.SYSTEM_HIGH_PRIORITY
+        }
+        broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
+    }
+
     private class TestBroadcastDispatcher(
         context: Context,
         mainHandler: Handler,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
deleted file mode 100644
index 6302f9d..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.privacy
-
-import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
-import com.android.systemui.SysuiTestCase
-import org.junit.Assert.assertEquals
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-class PrivacyDialogBuilderTest : SysuiTestCase() {
-
-    companion object {
-        val TEST_UID = 1
-    }
-
-    @Test
-    fun testGenerateAppsList() {
-        val bar2 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
-                "Bar", TEST_UID, context))
-        val bar3 = PrivacyItem(Privacy.TYPE_LOCATION, PrivacyApplication(
-                "Bar", TEST_UID, context))
-        val foo0 = PrivacyItem(Privacy.TYPE_MICROPHONE, PrivacyApplication(
-                "Foo", TEST_UID, context))
-        val baz1 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
-                "Baz", TEST_UID, context))
-
-        val items = listOf(bar2, foo0, baz1, bar3)
-
-        val textBuilder = PrivacyDialogBuilder(context, items)
-
-        val list = textBuilder.appsAndTypes
-        assertEquals(3, list.size)
-        val appsList = list.map { it.first }
-        val typesList = list.map { it.second }
-        // List is sorted by number of types and then by types
-        assertEquals(listOf("Bar", "Baz", "Foo"), appsList.map { it.packageName })
-        assertEquals(listOf(Privacy.TYPE_CAMERA, Privacy.TYPE_LOCATION), typesList[0])
-        assertEquals(listOf(Privacy.TYPE_CAMERA), typesList[1])
-        assertEquals(listOf(Privacy.TYPE_MICROPHONE), typesList[2])
-    }
-
-    @Test
-    fun testOrder() {
-        // We want location to always go last, so it will go in the "+ other apps"
-        val appCamera = PrivacyItem(PrivacyType.TYPE_CAMERA,
-                PrivacyApplication("Camera", TEST_UID, context))
-        val appMicrophone =
-                PrivacyItem(PrivacyType.TYPE_MICROPHONE,
-                        PrivacyApplication("Microphone", TEST_UID, context))
-        val appLocation =
-                PrivacyItem(PrivacyType.TYPE_LOCATION,
-                        PrivacyApplication("Location", TEST_UID, context))
-
-        val items = listOf(appLocation, appMicrophone, appCamera)
-        val textBuilder = PrivacyDialogBuilder(context, items)
-        val appList = textBuilder.appsAndTypes.map { it.first }.map { it.packageName }
-        assertEquals(listOf("Camera", "Microphone", "Location"), appList)
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
deleted file mode 100644
index 20148e1..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.privacy
-
-import android.app.ActivityManager
-import android.app.AppOpsManager
-import android.content.Context
-import android.content.Intent
-import android.content.pm.UserInfo
-import android.os.Handler
-import android.os.UserHandle
-import android.os.UserManager
-import android.provider.DeviceConfig
-import android.provider.Settings.RESET_MODE_PACKAGE_DEFAULTS
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.testing.TestableLooper.RunWithLooper
-import androidx.test.filters.SmallTest
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
-import com.android.systemui.Dependency
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.appops.AppOpItem
-import com.android.systemui.appops.AppOpsController
-import com.android.systemui.broadcast.BroadcastDispatcher
-import org.hamcrest.Matchers.hasItem
-import org.hamcrest.Matchers.not
-import org.hamcrest.Matchers.nullValue
-import org.junit.After
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertThat
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyList
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito.atLeastOnce
-import org.mockito.Mockito.doAnswer
-import org.mockito.Mockito.doReturn
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.MockitoAnnotations
-
-@RunWith(AndroidTestingRunner::class)
-@SmallTest
-@RunWithLooper
-@Ignore
-class PrivacyItemControllerTest : SysuiTestCase() {
-
-    companion object {
-        val CURRENT_USER_ID = ActivityManager.getCurrentUser()
-        val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE
-        const val SYSTEM_UID = 1000
-        const val TEST_PACKAGE_NAME = "test"
-        const val DEVICE_SERVICES_STRING = "Device services"
-        const val TAG = "PrivacyItemControllerTest"
-        fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
-    }
-
-    @Mock
-    private lateinit var appOpsController: AppOpsController
-    @Mock
-    private lateinit var callback: PrivacyItemController.Callback
-    @Mock
-    private lateinit var userManager: UserManager
-    @Mock
-    private lateinit var broadcastDispatcher: BroadcastDispatcher
-    @Captor
-    private lateinit var argCaptor: ArgumentCaptor<List<PrivacyItem>>
-    @Captor
-    private lateinit var argCaptorCallback: ArgumentCaptor<AppOpsController.Callback>
-
-    private lateinit var testableLooper: TestableLooper
-    private lateinit var privacyItemController: PrivacyItemController
-    private lateinit var handler: Handler
-
-    fun PrivacyItemController(context: Context) =
-            PrivacyItemController(context, appOpsController, handler, handler, broadcastDispatcher)
-
-    @Before
-    fun setup() {
-        MockitoAnnotations.initMocks(this)
-        testableLooper = TestableLooper.get(this)
-        handler = Handler(testableLooper.looper)
-
-        appOpsController = mDependency.injectMockDependency(AppOpsController::class.java)
-        mDependency.injectTestDependency(Dependency.MAIN_HANDLER, handler)
-        mContext.addMockSystemService(UserManager::class.java, userManager)
-        mContext.getOrCreateTestableResources().addOverride(R.string.device_services,
-                DEVICE_SERVICES_STRING)
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
-                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
-                "true", false)
-
-        doReturn(listOf(object : UserInfo() {
-            init {
-                id = CURRENT_USER_ID
-            }
-        })).`when`(userManager).getProfiles(anyInt())
-
-        privacyItemController = PrivacyItemController(mContext)
-    }
-
-    @After
-    fun tearDown() {
-        DeviceConfig.resetToDefaults(RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_PRIVACY)
-    }
-
-    @Test
-    fun testSetListeningTrueByAddingCallback() {
-        privacyItemController.addCallback(callback)
-        testableLooper.processAllMessages()
-        verify(appOpsController).addCallback(eq(PrivacyItemController.OPS),
-                any(AppOpsController.Callback::class.java))
-        testableLooper.processAllMessages()
-        verify(callback).privacyChanged(anyList())
-    }
-
-    @Test
-    fun testSetListeningFalseByRemovingLastCallback() {
-        privacyItemController.addCallback(callback)
-        testableLooper.processAllMessages()
-        verify(appOpsController, never()).removeCallback(any(IntArray::class.java),
-                any(AppOpsController.Callback::class.java))
-        privacyItemController.removeCallback(callback)
-        testableLooper.processAllMessages()
-        verify(appOpsController).removeCallback(eq(PrivacyItemController.OPS),
-                any(AppOpsController.Callback::class.java))
-        verify(callback).privacyChanged(emptyList())
-    }
-
-    @Test
-    fun testDistinctItems() {
-        doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
-                AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 1)))
-                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
-
-        privacyItemController.addCallback(callback)
-        testableLooper.processAllMessages()
-        verify(callback).privacyChanged(capture(argCaptor))
-        assertEquals(1, argCaptor.value.size)
-    }
-
-    @Test
-    fun testSystemApps() {
-        doReturn(listOf(AppOpItem(AppOpsManager.OP_COARSE_LOCATION, SYSTEM_UID, TEST_PACKAGE_NAME,
-                0))).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
-        privacyItemController.addCallback(callback)
-        testableLooper.processAllMessages()
-        verify(callback).privacyChanged(capture(argCaptor))
-        assertEquals(1, argCaptor.value.size)
-        assertEquals(context.getString(R.string.device_services),
-                argCaptor.value[0].application.applicationName)
-    }
-
-    @Test
-    fun testRegisterReceiver_allUsers() {
-        privacyItemController.addCallback(callback)
-        testableLooper.processAllMessages()
-        verify(broadcastDispatcher, atLeastOnce()).registerReceiver(
-                eq(privacyItemController.userSwitcherReceiver), any(), eq(null), eq(UserHandle.ALL))
-        verify(broadcastDispatcher, never())
-                .unregisterReceiver(eq(privacyItemController.userSwitcherReceiver))
-    }
-
-    @Test
-    fun testReceiver_ACTION_USER_FOREGROUND() {
-        privacyItemController.userSwitcherReceiver.onReceive(context,
-                Intent(Intent.ACTION_USER_FOREGROUND))
-        verify(userManager).getProfiles(anyInt())
-    }
-
-    @Test
-    fun testReceiver_ACTION_MANAGED_PROFILE_ADDED() {
-        privacyItemController.userSwitcherReceiver.onReceive(context,
-                Intent(Intent.ACTION_MANAGED_PROFILE_ADDED))
-        verify(userManager).getProfiles(anyInt())
-    }
-
-    @Test
-    fun testReceiver_ACTION_MANAGED_PROFILE_REMOVED() {
-        privacyItemController.userSwitcherReceiver.onReceive(context,
-                Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED))
-        verify(userManager).getProfiles(anyInt())
-    }
-
-    @Test
-    fun testAddMultipleCallbacks() {
-        val otherCallback = mock(PrivacyItemController.Callback::class.java)
-        privacyItemController.addCallback(callback)
-        testableLooper.processAllMessages()
-        verify(callback).privacyChanged(anyList())
-
-        privacyItemController.addCallback(otherCallback)
-        testableLooper.processAllMessages()
-        verify(otherCallback).privacyChanged(anyList())
-        // Adding a callback should not unnecessarily call previous ones
-        verifyNoMoreInteractions(callback)
-    }
-
-    @Test
-    fun testMultipleCallbacksAreUpdated() {
-        doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
-
-        val otherCallback = mock(PrivacyItemController.Callback::class.java)
-        privacyItemController.addCallback(callback)
-        privacyItemController.addCallback(otherCallback)
-        testableLooper.processAllMessages()
-        reset(callback)
-        reset(otherCallback)
-
-        verify(appOpsController).addCallback(any<IntArray>(), capture(argCaptorCallback))
-        argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
-        testableLooper.processAllMessages()
-        verify(callback).privacyChanged(anyList())
-        verify(otherCallback).privacyChanged(anyList())
-    }
-
-    @Test
-    fun testRemoveCallback() {
-        doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
-        val otherCallback = mock(PrivacyItemController.Callback::class.java)
-        privacyItemController.addCallback(callback)
-        privacyItemController.addCallback(otherCallback)
-        testableLooper.processAllMessages()
-        reset(callback)
-        reset(otherCallback)
-
-        verify(appOpsController).addCallback(any<IntArray>(), capture(argCaptorCallback))
-        privacyItemController.removeCallback(callback)
-        argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
-        testableLooper.processAllMessages()
-        verify(callback, never()).privacyChanged(anyList())
-        verify(otherCallback).privacyChanged(anyList())
-    }
-
-    @Test
-    fun testListShouldNotHaveNull() {
-        doReturn(listOf(AppOpItem(AppOpsManager.OP_ACTIVATE_VPN, TEST_UID, "", 0),
-                        AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
-                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
-        privacyItemController.addCallback(callback)
-        testableLooper.processAllMessages()
-
-        verify(callback).privacyChanged(capture(argCaptor))
-        assertEquals(1, argCaptor.value.size)
-        assertThat(argCaptor.value, not(hasItem(nullValue())))
-    }
-
-    @Test
-    fun testListShouldBeCopy() {
-        val list = listOf(PrivacyItem(PrivacyType.TYPE_CAMERA,
-                PrivacyApplication("", TEST_UID, mContext)))
-        privacyItemController.privacyList = list
-        val privacyList = privacyItemController.privacyList
-        assertEquals(list, privacyList)
-        assertTrue(list !== privacyList)
-    }
-
-    @Test
-    fun testNotListeningWhenIndicatorsDisabled() {
-        val properties = getProperties(
-                DeviceConfig.NAMESPACE_PRIVACY,
-                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
-                false)
-        privacyItemController.devicePropertiesChangedListener.onPropertiesChanged(properties)
-        privacyItemController.addCallback(callback)
-        testableLooper.processAllMessages()
-        verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS),
-                any(AppOpsController.Callback::class.java))
-    }
-
-    private fun getProperties(namespace: String, name: String, value: Boolean):
-            DeviceConfig.Properties {
-        val properties = mock<DeviceConfig.Properties>(DeviceConfig.Properties::class.java)
-        doReturn(namespace).`when`(properties).getNamespace()
-        doReturn(setOf(name)).`when`(properties).getKeyset()
-        doAnswer {
-            val key: String = it.getArgument(0)
-            val defaultValue: Boolean = it.getArgument(1)
-            if (name.equals(key, ignoreCase = true)) {
-                value
-            } else {
-                defaultValue
-            }
-        }.`when`(properties).getBoolean(anyString(), anyBoolean())
-        return properties
-    }
-}
\ No newline at end of file
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/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 5983785..2175ca0 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -46,7 +46,6 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.os.AtomicDirectory;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
@@ -128,6 +127,7 @@
 
     private static final String PARAMETER_DELIMITER = ",";
     private static final String PARAMETER_ASSIGNMENT = "=";
+    private static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
 
     @GuardedBy("mLock")
     private @NonNull LinkedList<HistoricalOps> mPendingWrites = new LinkedList<>();
@@ -701,7 +701,7 @@
     private static boolean isApiEnabled() {
         return Binder.getCallingUid() == Process.myUid()
                 || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false);
+                PROPERTY_PERMISSIONS_HUB_ENABLED, false);
     }
 
     private static final class Persistence {
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/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 3c76c67..293fd40 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -324,6 +324,7 @@
     static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
 
     static final int INVALID_UID = -1;
+    static final String ROOT_PKG = "root";
 
     static final boolean ENABLE_BLOCKED_TOASTS = true;
 
@@ -7779,7 +7780,8 @@
 
     protected boolean isUidSystemOrPhone(int uid) {
         final int appid = UserHandle.getAppId(uid);
-        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
+        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID
+                || uid == Process.ROOT_UID);
     }
 
     // TODO: Most calls should probably move to isCallerSystem.
@@ -7788,7 +7790,8 @@
     }
 
     private void checkCallerIsSystemOrShell() {
-        if (Binder.getCallingUid() == Process.SHELL_UID) {
+        int callingUid = Binder.getCallingUid();
+        if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
             return;
         }
         checkCallerIsSystem();
@@ -7802,7 +7805,8 @@
     }
 
     private void checkCallerIsSystemOrSystemUiOrShell() {
-        if (Binder.getCallingUid() == Process.SHELL_UID) {
+        int callingUid = Binder.getCallingUid();
+        if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
             return;
         }
         if (isCallerSystemOrPhone()) {
@@ -7877,6 +7881,9 @@
     }
 
     private void checkCallerIsSameApp(String pkg, int uid, int userId) {
+        if (uid == Process.ROOT_UID && ROOT_PKG.equals(pkg)) {
+            return;
+        }
         try {
             ApplicationInfo ai = mPackageManager.getApplicationInfo(
                     pkg, 0, userId);
diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java
index 50f16bc..2b5ba25 100644
--- a/services/core/java/com/android/server/notification/NotificationShellCmd.java
+++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java
@@ -125,9 +125,13 @@
         final int callingUid = Binder.getCallingUid();
         long identity = Binder.clearCallingIdentity();
         try {
-            String[] packages = mPm.getPackagesForUid(callingUid);
-            if (packages != null && packages.length > 0) {
-                callingPackage = packages[0];
+            if (callingUid == Process.ROOT_UID) {
+                callingPackage = NotificationManagerService.ROOT_PKG;
+            } else {
+                String[] packages = mPm.getPackagesForUid(callingUid);
+                if (packages != null && packages.length > 0) {
+                    callingPackage = packages[0];
+                }
             }
         } catch (Exception e) {
             Slog.e(TAG, "failed to get caller pkg", e);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 93249e9..dceca0a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -494,7 +494,7 @@
             }
             // Only apps with INSTALL_PACKAGES are allowed to set an installer that is not the
             // caller.
-            if (!requestedInstallerPackageName.equals(installerPackageName)) {
+            if (!TextUtils.equals(requestedInstallerPackageName, installerPackageName)) {
                 if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
                         != PackageManager.PERMISSION_GRANTED) {
                     mAppOps.checkPackage(callingUid, requestedInstallerPackageName);
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/timedetector/SimpleTimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
index 3c79b23..0b970bf 100644
--- a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
@@ -23,10 +23,13 @@
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.PhoneTimeSuggestion;
 import android.content.Intent;
+import android.util.LocalLog;
 import android.util.Slog;
 import android.util.TimestampedValue;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -34,13 +37,14 @@
 
 /**
  * An implementation of TimeDetectorStrategy that passes only NITZ suggestions to
- * {@link AlarmManager}. The TimeDetectorService handles thread safety: all calls to
- * this class can be assumed to be single threaded (though the thread used may vary).
+ * {@link AlarmManager}.
+ *
+ * <p>Most public methods are marked synchronized to ensure thread safety around internal state.
  */
-// @NotThreadSafe
 public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
 
-    private final static String TAG = "timedetector.SimpleTimeDetectorStrategy";
+    private static final boolean DBG = false;
+    private static final String LOG_TAG = "SimpleTimeDetectorStrategy";
 
     @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL })
     @Retention(RetentionPolicy.SOURCE)
@@ -61,6 +65,9 @@
      */
     private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000;
 
+    // A log for changes made to the system clock and why.
+    @NonNull private final LocalLog mTimeChangesLog = new LocalLog(30);
+
     // @NonNull after initialize()
     private Callback mCallback;
 
@@ -80,7 +87,7 @@
     }
 
     @Override
-    public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
+    public synchronized void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
         // NITZ logic
 
         // Empty suggestions are just ignored as we don't currently keep track of suggestion origin.
@@ -103,7 +110,7 @@
     }
 
     @Override
-    public void suggestManualTime(ManualTimeSuggestion timeSuggestion) {
+    public synchronized void suggestManualTime(ManualTimeSuggestion timeSuggestion) {
         final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime();
         setSystemClockIfRequired(ORIGIN_MANUAL, newUtcTime, timeSuggestion);
     }
@@ -116,7 +123,7 @@
                     newSuggestion.getUtcTime(), lastSuggestion.getUtcTime());
             if (referenceTimeDifference < 0 || referenceTimeDifference > Integer.MAX_VALUE) {
                 // Out of order or bogus.
-                Slog.w(TAG, "validateNewNitzTime: Bad NITZ signal received."
+                Slog.w(LOG_TAG, "Bad NITZ signal received."
                         + " referenceTimeDifference=" + referenceTimeDifference
                         + " lastSuggestion=" + lastSuggestion
                         + " newSuggestion=" + newSuggestion);
@@ -126,6 +133,7 @@
         return true;
     }
 
+    @GuardedBy("this")
     private void setSystemClockIfRequired(
             @Origin int origin, TimestampedValue<Long> time, Object cause) {
         // Historically, Android has sent a TelephonyIntents.ACTION_NETWORK_SET_TIME broadcast only
@@ -140,16 +148,20 @@
             mLastAutoSystemClockTimeSendNetworkBroadcast = sendNetworkBroadcast;
 
             if (!mCallback.isAutoTimeDetectionEnabled()) {
-                Slog.d(TAG, "setSystemClockIfRequired: Auto time detection is not enabled."
-                        + " time=" + time
-                        + ", cause=" + cause);
+                if (DBG) {
+                    Slog.d(LOG_TAG, "Auto time detection is not enabled."
+                            + " time=" + time
+                            + ", cause=" + cause);
+                }
                 return;
             }
         } else {
             if (mCallback.isAutoTimeDetectionEnabled()) {
-                Slog.d(TAG, "setSystemClockIfRequired: Auto time detection is enabled."
-                        + " time=" + time
-                        + ", cause=" + cause);
+                if (DBG) {
+                    Slog.d(LOG_TAG, "Auto time detection is enabled."
+                            + " time=" + time
+                            + ", cause=" + cause);
+                }
                 return;
             }
         }
@@ -167,7 +179,7 @@
                             mLastAutoSystemClockTimeSet, elapsedRealtimeMillis);
                     long absSystemClockDifference = Math.abs(expectedTimeMillis - actualTimeMillis);
                     if (absSystemClockDifference > SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS) {
-                        Slog.w(TAG,
+                        Slog.w(LOG_TAG,
                                 "System clock has not tracked elapsed real time clock. A clock may"
                                         + " be inaccurate or something unexpectedly set the system"
                                         + " clock."
@@ -190,9 +202,10 @@
     }
 
     @Override
-    public void handleAutoTimeDetectionToggle(boolean enabled) {
+    public synchronized void handleAutoTimeDetectionChanged() {
         // If automatic time detection is enabled we update the system clock instantly if we can.
         // Conversely, if automatic time detection is disabled we leave the clock as it is.
+        boolean enabled = mCallback.isAutoTimeDetectionEnabled();
         if (enabled) {
             if (mLastAutoSystemClockTime != null) {
                 // Only send the network broadcast if the last candidate would have caused one.
@@ -218,14 +231,27 @@
     }
 
     @Override
-    public void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
+    public synchronized void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
         pw.println("mLastPhoneSuggestion=" + mLastPhoneSuggestion);
         pw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
         pw.println("mLastAutoSystemClockTime=" + mLastAutoSystemClockTime);
         pw.println("mLastAutoSystemClockTimeSendNetworkBroadcast="
                 + mLastAutoSystemClockTimeSendNetworkBroadcast);
+
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+
+        ipw.println("TimeDetectorStrategyImpl logs:");
+        ipw.increaseIndent(); // level 1
+
+        ipw.println("Time change log:");
+        ipw.increaseIndent(); // level 2
+        mTimeChangesLog.dump(ipw);
+        ipw.decreaseIndent(); // level 2
+
+        ipw.decreaseIndent(); // level 1
     }
 
+    @GuardedBy("this")
     private void adjustAndSetDeviceSystemClock(
             TimestampedValue<Long> newTime, boolean sendNetworkBroadcast,
             long elapsedRealtimeMillis, long actualSystemClockMillis, Object cause) {
@@ -238,21 +264,27 @@
         long absTimeDifference = Math.abs(newSystemClockMillis - actualSystemClockMillis);
         long systemClockUpdateThreshold = mCallback.systemClockUpdateThresholdMillis();
         if (absTimeDifference < systemClockUpdateThreshold) {
-            Slog.d(TAG, "adjustAndSetDeviceSystemClock: Not setting system clock. New time and"
-                    + " system clock are close enough."
-                    + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
-                    + " newTime=" + newTime
-                    + " cause=" + cause
-                    + " systemClockUpdateThreshold=" + systemClockUpdateThreshold
-                    + " absTimeDifference=" + absTimeDifference);
+            if (DBG) {
+                Slog.d(LOG_TAG, "Not setting system clock. New time and"
+                        + " system clock are close enough."
+                        + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+                        + " newTime=" + newTime
+                        + " cause=" + cause
+                        + " systemClockUpdateThreshold=" + systemClockUpdateThreshold
+                        + " absTimeDifference=" + absTimeDifference);
+            }
             return;
         }
 
-        Slog.d(TAG, "Setting system clock using time=" + newTime
+        mCallback.setSystemClock(newSystemClockMillis);
+        String logMsg = "Set system clock using time=" + newTime
                 + " cause=" + cause
                 + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
-                + " newTimeMillis=" + newSystemClockMillis);
-        mCallback.setSystemClock(newSystemClockMillis);
+                + " newSystemClockMillis=" + newSystemClockMillis;
+        if (DBG) {
+            Slog.d(LOG_TAG, logMsg);
+        }
+        mTimeChangesLog.log(logMsg);
 
         // CLOCK_PARANOIA : Record the last time this class set the system clock.
         mLastAutoSystemClockTimeSet = newTime;
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 7f5b403..34400ff 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -24,10 +24,9 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
-import android.os.Binder;
+import android.os.Handler;
 import android.provider.Settings;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
 import com.android.server.FgThread;
@@ -39,7 +38,7 @@
 import java.util.Objects;
 
 public final class TimeDetectorService extends ITimeDetectorService.Stub {
-    private static final String TAG = "timedetector.TimeDetectorService";
+    private static final String TAG = "TimeDetectorService";
 
     public static class Lifecycle extends SystemService {
 
@@ -57,29 +56,25 @@
         }
     }
 
+    @NonNull private final Handler mHandler;
     @NonNull private final Context mContext;
     @NonNull private final Callback mCallback;
-
-    // The lock used when call the strategy to ensure thread safety.
-    @NonNull private final Object mStrategyLock = new Object();
-
-    @GuardedBy("mStrategyLock")
     @NonNull private final TimeDetectorStrategy mTimeDetectorStrategy;
 
     private static TimeDetectorService create(@NonNull Context context) {
-        final TimeDetectorStrategy timeDetector = new SimpleTimeDetectorStrategy();
-        final TimeDetectorStrategyCallbackImpl callback =
-                new TimeDetectorStrategyCallbackImpl(context);
+        TimeDetectorStrategy timeDetector = new SimpleTimeDetectorStrategy();
+        TimeDetectorStrategyCallbackImpl callback = new TimeDetectorStrategyCallbackImpl(context);
         timeDetector.initialize(callback);
 
+        Handler handler = FgThread.getHandler();
         TimeDetectorService timeDetectorService =
-                new TimeDetectorService(context, callback, timeDetector);
+                new TimeDetectorService(context, handler, callback, timeDetector);
 
         // Wire up event listening.
         ContentResolver contentResolver = context.getContentResolver();
         contentResolver.registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
-                new ContentObserver(FgThread.getHandler()) {
+                new ContentObserver(handler) {
                     public void onChange(boolean selfChange) {
                         timeDetectorService.handleAutoTimeDetectionToggle();
                     }
@@ -89,9 +84,10 @@
     }
 
     @VisibleForTesting
-    public TimeDetectorService(@NonNull Context context, @NonNull Callback callback,
-            @NonNull TimeDetectorStrategy timeDetectorStrategy) {
+    public TimeDetectorService(@NonNull Context context, @NonNull Handler handler,
+            @NonNull Callback callback, @NonNull TimeDetectorStrategy timeDetectorStrategy) {
         mContext = Objects.requireNonNull(context);
+        mHandler = Objects.requireNonNull(handler);
         mCallback = Objects.requireNonNull(callback);
         mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy);
     }
@@ -101,14 +97,7 @@
         enforceSuggestPhoneTimePermission();
         Objects.requireNonNull(timeSignal);
 
-        long idToken = Binder.clearCallingIdentity();
-        try {
-            synchronized (mStrategyLock) {
-                mTimeDetectorStrategy.suggestPhoneTime(timeSignal);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(idToken);
-        }
+        mHandler.post(() -> mTimeDetectorStrategy.suggestPhoneTime(timeSignal));
     }
 
     @Override
@@ -116,22 +105,12 @@
         enforceSuggestManualTimePermission();
         Objects.requireNonNull(timeSignal);
 
-        long idToken = Binder.clearCallingIdentity();
-        try {
-            synchronized (mStrategyLock) {
-                mTimeDetectorStrategy.suggestManualTime(timeSignal);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(idToken);
-        }
+        mHandler.post(() -> mTimeDetectorStrategy.suggestManualTime(timeSignal));
     }
 
     @VisibleForTesting
     public void handleAutoTimeDetectionToggle() {
-        synchronized (mStrategyLock) {
-            final boolean timeDetectionEnabled = mCallback.isAutoTimeDetectionEnabled();
-            mTimeDetectorStrategy.handleAutoTimeDetectionToggle(timeDetectionEnabled);
-        }
+        mHandler.post(mTimeDetectorStrategy::handleAutoTimeDetectionChanged);
     }
 
     @Override
@@ -139,9 +118,7 @@
             @Nullable String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
-        synchronized (mStrategyLock) {
-            mTimeDetectorStrategy.dump(pw, args);
-        }
+        mTimeDetectorStrategy.dump(pw, args);
     }
 
     private void enforceSuggestPhoneTimePermission() {
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index b60cebf..32cee2d 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -27,12 +27,14 @@
 
 /**
  * The interface for classes that implement the time detection algorithm used by the
- * TimeDetectorService. The TimeDetectorService handles thread safety: all calls to implementations
- * of this interface can be assumed to be single threaded (though the thread used may vary).
+ * TimeDetectorService.
+ *
+ * <p>Most calls will be handled by a single thread but that is not true for all calls. For example
+ * {@link #dump(PrintWriter, String[])}) may be called on a different thread so implementations must
+ * handle thread safety.
  *
  * @hide
  */
-// @NotThreadSafe
 public interface TimeDetectorStrategy {
 
     /**
@@ -79,7 +81,7 @@
     void suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion);
 
     /** Handle the auto-time setting being toggled on or off. */
-    void handleAutoTimeDetectionToggle(boolean enabled);
+    void handleAutoTimeDetectionChanged();
 
     /** Dump debug information. */
     void dump(@NonNull PrintWriter pw, @Nullable String[] args);
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
index 23746ac..e034ad4 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
@@ -43,7 +43,7 @@
     }
 
     @Override
-    public boolean isTimeZoneDetectionEnabled() {
+    public boolean isAutoTimeZoneDetectionEnabled() {
         return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE, 1 /* default */) > 0;
     }
 
@@ -66,14 +66,16 @@
     }
 
     @Override
-    public void setDeviceTimeZone(String zoneId) {
+    public void setDeviceTimeZone(String zoneId, boolean sendNetworkBroadcast) {
         AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class);
         alarmManager.setTimeZone(zoneId);
 
-        // TODO Nothing in the platform appears to listen for this. Remove it.
-        Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-        intent.putExtra("time-zone", zoneId);
-        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+        if (sendNetworkBroadcast) {
+            // TODO Nothing in the platform appears to listen for this. Remove it.
+            Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
+            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+            intent.putExtra("time-zone", zoneId);
+            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index 558aa9e..9a1fe65 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.timezonedetector.ITimeZoneDetectorService;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
 import android.app.timezonedetector.PhoneTimeZoneSuggestion;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -28,7 +29,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
-import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.FgThread;
 import com.android.server.SystemService;
 
@@ -75,7 +75,7 @@
                 Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
                 new ContentObserver(handler) {
                     public void onChange(boolean selfChange) {
-                        timeZoneDetectorStrategy.handleTimeZoneDetectionChange();
+                        timeZoneDetectorStrategy.handleAutoTimeZoneDetectionChange();
                     }
                 });
 
@@ -91,8 +91,16 @@
     }
 
     @Override
+    public void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) {
+        enforceSuggestManualTimeZonePermission();
+        Objects.requireNonNull(timeZoneSuggestion);
+
+        mHandler.post(() -> mTimeZoneDetectorStrategy.suggestManualTimeZone(timeZoneSuggestion));
+    }
+
+    @Override
     public void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion) {
-        enforceSetTimeZonePermission();
+        enforceSuggestPhoneTimeZonePermission();
         Objects.requireNonNull(timeZoneSuggestion);
 
         mHandler.post(() -> mTimeZoneDetectorStrategy.suggestPhoneTimeZone(timeZoneSuggestion));
@@ -103,13 +111,17 @@
             @Nullable String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
-        mTimeZoneDetectorStrategy.dumpState(pw);
-        mTimeZoneDetectorStrategy.dumpLogs(new IndentingPrintWriter(pw, " "));
+        mTimeZoneDetectorStrategy.dumpState(pw, args);
     }
 
-    private void enforceSetTimeZonePermission() {
+    private void enforceSuggestPhoneTimeZonePermission() {
         mContext.enforceCallingPermission(
                 android.Manifest.permission.SET_TIME_ZONE, "set time zone");
     }
+
+    private void enforceSuggestManualTimeZonePermission() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.SET_TIME_ZONE, "set time zone");
+    }
 }
 
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index e24c089..5db12c7 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -21,8 +21,10 @@
 import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
 import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
 import android.app.timezonedetector.PhoneTimeZoneSuggestion;
 import android.content.Context;
 import android.util.ArrayMap;
@@ -34,22 +36,34 @@
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.LinkedList;
 import java.util.Map;
 import java.util.Objects;
 
 /**
- * A singleton, stateful time zone detection strategy that is aware of multiple phone devices. It
- * keeps track of the most recent suggestion from each phone and it uses the best based on a scoring
- * algorithm. If several phones provide the same score then the phone with the lowest numeric ID
- * "wins". If the situation changes and it is no longer possible to be confident about the time
- * zone, phones must submit an empty suggestion in order to "withdraw" their previous suggestion.
+ * A singleton, stateful time zone detection strategy that is aware of user (manual) suggestions and
+ * suggestions from multiple phone devices. Suggestions are acted on or ignored as needed, dependent
+ * on the current "auto time zone detection" setting.
+ *
+ * <p>For automatic detection it keeps track of the most recent suggestion from each phone it uses
+ * the best suggestion based on a scoring algorithm. If several phones provide the same score then
+ * the phone with the lowest numeric ID "wins". If the situation changes and it is no longer
+ * possible to be confident about the time zone, phones must submit an empty suggestion in order to
+ * "withdraw" their previous suggestion.
  */
 public class TimeZoneDetectorStrategy {
 
     /**
      * Used by {@link TimeZoneDetectorStrategy} to interact with the surrounding service. It can be
      * faked for tests.
+     *
+     * <p>Note: Because the system properties-derived values like
+     * {@link #isAutoTimeZoneDetectionEnabled()}, {@link #isAutoTimeZoneDetectionEnabled()},
+     * {@link #getDeviceTimeZone()} can be modified independently and from different threads (and
+     * processes!), their use are prone to race conditions. That will be true until the
+     * responsibility for setting their values is moved to {@link TimeZoneDetectorStrategy}.
      */
     @VisibleForTesting
     public interface Callback {
@@ -57,7 +71,7 @@
         /**
          * Returns true if automatic time zone detection is enabled in settings.
          */
-        boolean isTimeZoneDetectionEnabled();
+        boolean isAutoTimeZoneDetectionEnabled();
 
         /**
          * Returns true if the device has had an explicit time zone set.
@@ -72,22 +86,34 @@
         /**
          * Sets the device's time zone.
          */
-        void setDeviceTimeZone(@NonNull String zoneId);
+        void setDeviceTimeZone(@NonNull String zoneId, boolean sendNetworkBroadcast);
     }
 
-    static final String LOG_TAG = "TimeZoneDetectorStrategy";
-    static final boolean DBG = false;
+    private static final String LOG_TAG = "TimeZoneDetectorStrategy";
+    private static final boolean DBG = false;
+
+    @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Origin {}
+
+    /** Used when a time value originated from a telephony signal. */
+    @Origin
+    private static final int ORIGIN_PHONE = 1;
+
+    /** Used when a time value originated from a user / manual settings. */
+    @Origin
+    private static final int ORIGIN_MANUAL = 2;
 
     /**
-     * The abstract score for an empty or invalid suggestion.
+     * The abstract score for an empty or invalid phone suggestion.
      *
-     * Used to score suggestions where there is no zone.
+     * Used to score phone suggestions where there is no zone.
      */
     @VisibleForTesting
-    public static final int SCORE_NONE = 0;
+    public static final int PHONE_SCORE_NONE = 0;
 
     /**
-     * The abstract score for a low quality suggestion.
+     * The abstract score for a low quality phone suggestion.
      *
      * Used to score suggestions where:
      * The suggested zone ID is one of several possibilities, and the possibilities have different
@@ -96,10 +122,10 @@
      * You would have to be quite desperate to want to use this choice.
      */
     @VisibleForTesting
-    public static final int SCORE_LOW = 1;
+    public static final int PHONE_SCORE_LOW = 1;
 
     /**
-     * The abstract score for a medium quality suggestion.
+     * The abstract score for a medium quality phone suggestion.
      *
      * Used for:
      * The suggested zone ID is one of several possibilities but at least the possibilities have the
@@ -107,33 +133,36 @@
      * switch to DST at the wrong time and (for example) their calendar events.
      */
     @VisibleForTesting
-    public static final int SCORE_MEDIUM = 2;
+    public static final int PHONE_SCORE_MEDIUM = 2;
 
     /**
-     * The abstract score for a high quality suggestion.
+     * The abstract score for a high quality phone suggestion.
      *
      * Used for:
      * The suggestion was for one zone ID and the answer was unambiguous and likely correct given
      * the info available.
      */
     @VisibleForTesting
-    public static final int SCORE_HIGH = 3;
+    public static final int PHONE_SCORE_HIGH = 3;
 
     /**
-     * The abstract score for a highest quality suggestion.
+     * The abstract score for a highest quality phone suggestion.
      *
      * Used for:
      * Suggestions that must "win" because they constitute test or emulator zone ID.
      */
     @VisibleForTesting
-    public static final int SCORE_HIGHEST = 4;
+    public static final int PHONE_SCORE_HIGHEST = 4;
 
-    /** The threshold at which suggestions are good enough to use to set the device's time zone. */
+    /**
+     * The threshold at which phone suggestions are good enough to use to set the device's time
+     * zone.
+     */
     @VisibleForTesting
-    public static final int SCORE_USAGE_THRESHOLD = SCORE_MEDIUM;
+    public static final int PHONE_SCORE_USAGE_THRESHOLD = PHONE_SCORE_MEDIUM;
 
     /** The number of previous phone suggestions to keep for each ID (for use during debugging). */
-    private static final int KEEP_SUGGESTION_HISTORY_SIZE = 30;
+    private static final int KEEP_PHONE_SUGGESTION_HISTORY_SIZE = 30;
 
     @NonNull
     private final Callback mCallback;
@@ -146,24 +175,16 @@
     private final LocalLog mTimeZoneChangesLog = new LocalLog(30);
 
     /**
-     * A mapping from phoneId to a linked list of time zone suggestions (the head being the latest).
-     * We typically expect one or two entries in this Map: devices will have a small number
+     * A mapping from phoneId to a linked list of phone time zone suggestions (the head being the
+     * latest). We typically expect one or two entries in this Map: devices will have a small number
      * of telephony devices and phoneIds are assumed to be stable. The LinkedList associated with
-     * the ID will not exceed {@link #KEEP_SUGGESTION_HISTORY_SIZE} in size.
+     * the ID will not exceed {@link #KEEP_PHONE_SUGGESTION_HISTORY_SIZE} in size.
      */
     @GuardedBy("this")
     private ArrayMap<Integer, LinkedList<QualifiedPhoneTimeZoneSuggestion>> mSuggestionByPhoneId =
             new ArrayMap<>();
 
     /**
-     * The most recent best guess of time zone from all phones. Can be {@code null} to indicate
-     * there would be no current suggestion.
-     */
-    @GuardedBy("this")
-    @Nullable
-    private QualifiedPhoneTimeZoneSuggestion mCurrentSuggestion;
-
-    /**
      * Creates a new instance of {@link TimeZoneDetectorStrategy}.
      */
     public static TimeZoneDetectorStrategy create(Context context) {
@@ -176,56 +197,68 @@
         mCallback = Objects.requireNonNull(callback);
     }
 
+    /** Process the suggested manually- / user-entered time zone. */
+    public synchronized void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion suggestion) {
+        Objects.requireNonNull(suggestion);
+
+        String timeZoneId = suggestion.getZoneId();
+        String cause = "Manual time suggestion received: suggestion=" + suggestion;
+        setDeviceTimeZoneIfRequired(ORIGIN_MANUAL, timeZoneId, cause);
+    }
+
     /**
      * Suggests a time zone for the device, or withdraws a previous suggestion if
      * {@link PhoneTimeZoneSuggestion#getZoneId()} is {@code null}. The suggestion is scoped to a
      * specific {@link PhoneTimeZoneSuggestion#getPhoneId() phone}.
      * See {@link PhoneTimeZoneSuggestion} for an explanation of the metadata associated with a
-     * suggestion. The service uses suggestions to decide whether to modify the device's time zone
+     * suggestion. The strategy uses suggestions to decide whether to modify the device's time zone
      * setting and what to set it to.
      */
-    public synchronized void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion newSuggestion) {
+    public synchronized void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion suggestion) {
         if (DBG) {
-            Slog.d(LOG_TAG, "suggestPhoneTimeZone: newSuggestion=" + newSuggestion);
+            Slog.d(LOG_TAG, "Phone suggestion received. newSuggestion=" + suggestion);
         }
-        Objects.requireNonNull(newSuggestion);
+        Objects.requireNonNull(suggestion);
 
-        int score = scoreSuggestion(newSuggestion);
+        // Score the suggestion.
+        int score = scorePhoneSuggestion(suggestion);
         QualifiedPhoneTimeZoneSuggestion scoredSuggestion =
-                new QualifiedPhoneTimeZoneSuggestion(newSuggestion, score);
+                new QualifiedPhoneTimeZoneSuggestion(suggestion, score);
 
-        // Record the suggestion against the correct phoneId.
+        // Store the suggestion against the correct phoneId.
         LinkedList<QualifiedPhoneTimeZoneSuggestion> suggestions =
-                mSuggestionByPhoneId.get(newSuggestion.getPhoneId());
+                mSuggestionByPhoneId.get(suggestion.getPhoneId());
         if (suggestions == null) {
             suggestions = new LinkedList<>();
-            mSuggestionByPhoneId.put(newSuggestion.getPhoneId(), suggestions);
+            mSuggestionByPhoneId.put(suggestion.getPhoneId(), suggestions);
         }
         suggestions.addFirst(scoredSuggestion);
-        if (suggestions.size() > KEEP_SUGGESTION_HISTORY_SIZE) {
+        if (suggestions.size() > KEEP_PHONE_SUGGESTION_HISTORY_SIZE) {
             suggestions.removeLast();
         }
 
-        // Now run the competition between the phones' suggestions.
-        doTimeZoneDetection();
+        // Now perform auto time zone detection. The new suggestion may be used to modify the time
+        // zone setting.
+        String reason = "New phone time suggested. suggestion=" + suggestion;
+        doAutoTimeZoneDetection(reason);
     }
 
-    private static int scoreSuggestion(@NonNull PhoneTimeZoneSuggestion suggestion) {
+    private static int scorePhoneSuggestion(@NonNull PhoneTimeZoneSuggestion suggestion) {
         int score;
         if (suggestion.getZoneId() == null) {
-            score = SCORE_NONE;
+            score = PHONE_SCORE_NONE;
         } else if (suggestion.getMatchType() == MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY
                 || suggestion.getMatchType() == MATCH_TYPE_EMULATOR_ZONE_ID) {
             // Handle emulator / test cases : These suggestions should always just be used.
-            score = SCORE_HIGHEST;
+            score = PHONE_SCORE_HIGHEST;
         } else if (suggestion.getQuality() == QUALITY_SINGLE_ZONE) {
-            score = SCORE_HIGH;
+            score = PHONE_SCORE_HIGH;
         } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET) {
             // The suggestion may be wrong, but at least the offset should be correct.
-            score = SCORE_MEDIUM;
+            score = PHONE_SCORE_MEDIUM;
         } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS) {
             // The suggestion has a good chance of being wrong.
-            score = SCORE_LOW;
+            score = PHONE_SCORE_LOW;
         } else {
             throw new AssertionError();
         }
@@ -235,47 +268,46 @@
     /**
      * Finds the best available time zone suggestion from all phones. If it is high-enough quality
      * and automatic time zone detection is enabled then it will be set on the device. The outcome
-     * can be that this service becomes / remains un-opinionated and nothing is set.
+     * can be that this strategy becomes / remains un-opinionated and nothing is set.
      */
     @GuardedBy("this")
-    private void doTimeZoneDetection() {
-        QualifiedPhoneTimeZoneSuggestion bestSuggestion = findBestSuggestion();
-        boolean timeZoneDetectionEnabled = mCallback.isTimeZoneDetectionEnabled();
+    private void doAutoTimeZoneDetection(@NonNull String detectionReason) {
+        if (!mCallback.isAutoTimeZoneDetectionEnabled()) {
+            // Avoid doing unnecessary work with this (race-prone) check.
+            return;
+        }
+
+        QualifiedPhoneTimeZoneSuggestion bestPhoneSuggestion = findBestPhoneSuggestion();
 
         // Work out what to do with the best suggestion.
-        if (bestSuggestion == null) {
-            // There is no suggestion. Become un-opinionated.
+        if (bestPhoneSuggestion == null) {
+            // There is no phone suggestion available at all. Become un-opinionated.
             if (DBG) {
-                Slog.d(LOG_TAG, "doTimeZoneDetection: No good suggestion."
-                        + " bestSuggestion=null"
-                        + ", timeZoneDetectionEnabled=" + timeZoneDetectionEnabled);
+                Slog.d(LOG_TAG, "Could not determine time zone: No best phone suggestion."
+                        + " detectionReason=" + detectionReason);
             }
-            mCurrentSuggestion = null;
             return;
         }
 
         // Special case handling for uninitialized devices. This should only happen once.
-        String newZoneId = bestSuggestion.suggestion.getZoneId();
+        String newZoneId = bestPhoneSuggestion.suggestion.getZoneId();
         if (newZoneId != null && !mCallback.isDeviceTimeZoneInitialized()) {
-            Slog.i(LOG_TAG, "doTimeZoneDetection: Device has no time zone set so might set the"
-                    + " device to the best available suggestion."
-                    + " bestSuggestion=" + bestSuggestion
-                    + ", timeZoneDetectionEnabled=" + timeZoneDetectionEnabled);
-
-            mCurrentSuggestion = bestSuggestion;
-            if (timeZoneDetectionEnabled) {
-                setDeviceTimeZone(bestSuggestion.suggestion);
-            }
+            String cause = "Device has no time zone set. Attempting to set the device to the best"
+                    + " available suggestion."
+                    + " bestPhoneSuggestion=" + bestPhoneSuggestion
+                    + ", detectionReason=" + detectionReason;
+            Slog.i(LOG_TAG, cause);
+            setDeviceTimeZoneIfRequired(ORIGIN_PHONE, newZoneId, cause);
             return;
         }
 
-        boolean suggestionGoodEnough = bestSuggestion.score >= SCORE_USAGE_THRESHOLD;
+        boolean suggestionGoodEnough = bestPhoneSuggestion.score >= PHONE_SCORE_USAGE_THRESHOLD;
         if (!suggestionGoodEnough) {
             if (DBG) {
-                Slog.d(LOG_TAG, "doTimeZoneDetection: Suggestion not good enough."
-                        + " bestSuggestion=" + bestSuggestion);
+                Slog.d(LOG_TAG, "Best suggestion not good enough."
+                        + " bestPhoneSuggestion=" + bestPhoneSuggestion
+                        + ", detectionReason=" + detectionReason);
             }
-            mCurrentSuggestion = null;
             return;
         }
 
@@ -283,63 +315,84 @@
         // zone ID.
         if (newZoneId == null) {
             Slog.w(LOG_TAG, "Empty zone suggestion scored higher than expected. This is an error:"
-                    + " bestSuggestion=" + bestSuggestion);
-            mCurrentSuggestion = null;
+                    + " bestPhoneSuggestion=" + bestPhoneSuggestion
+                    + " detectionReason=" + detectionReason);
             return;
         }
 
-        // There is a good suggestion. Store the suggestion and set the device time zone if
-        // settings allow.
-        mCurrentSuggestion = bestSuggestion;
-
-        // Only set the device time zone if time zone detection is enabled.
-        if (!timeZoneDetectionEnabled) {
-            if (DBG) {
-                Slog.d(LOG_TAG, "doTimeZoneDetection: Not setting the time zone because time zone"
-                        + " detection is disabled."
-                        + " bestSuggestion=" + bestSuggestion);
-            }
-            return;
-        }
-        PhoneTimeZoneSuggestion suggestion = bestSuggestion.suggestion;
-        setDeviceTimeZone(suggestion);
+        String zoneId = bestPhoneSuggestion.suggestion.getZoneId();
+        String cause = "Found good suggestion."
+                + ", bestPhoneSuggestion=" + bestPhoneSuggestion
+                + ", detectionReason=" + detectionReason;
+        setDeviceTimeZoneIfRequired(ORIGIN_PHONE, zoneId, cause);
     }
 
-    private void setDeviceTimeZone(@NonNull PhoneTimeZoneSuggestion suggestion) {
-        String currentZoneId = mCallback.getDeviceTimeZone();
-        String newZoneId = suggestion.getZoneId();
+    @GuardedBy("this")
+    private void setDeviceTimeZoneIfRequired(
+            @Origin int origin, @NonNull String newZoneId, @NonNull String cause) {
+        Objects.requireNonNull(newZoneId);
+        Objects.requireNonNull(cause);
 
-        // Paranoia: This should never happen.
-        if (newZoneId == null) {
-            Slog.w(LOG_TAG, "setDeviceTimeZone: Suggested zone is null."
-                    + " timeZoneSuggestion=" + suggestion);
-            return;
+        boolean sendNetworkBroadcast = (origin == ORIGIN_PHONE);
+        boolean isOriginAutomatic = isOriginAutomatic(origin);
+        if (isOriginAutomatic) {
+            if (!mCallback.isAutoTimeZoneDetectionEnabled()) {
+                if (DBG) {
+                    Slog.d(LOG_TAG, "Auto time zone detection is not enabled."
+                            + " origin=" + origin
+                            + ", newZoneId=" + newZoneId
+                            + ", cause=" + cause);
+                }
+                return;
+            }
+        } else {
+            if (mCallback.isAutoTimeZoneDetectionEnabled()) {
+                if (DBG) {
+                    Slog.d(LOG_TAG, "Auto time zone detection is enabled."
+                            + " origin=" + origin
+                            + ", newZoneId=" + newZoneId
+                            + ", cause=" + cause);
+                }
+                return;
+            }
         }
 
+        String currentZoneId = mCallback.getDeviceTimeZone();
+
         // Avoid unnecessary changes / intents.
         if (newZoneId.equals(currentZoneId)) {
             // No need to set the device time zone - the setting is already what we would be
             // suggesting.
             if (DBG) {
-                Slog.d(LOG_TAG, "setDeviceTimeZone: No need to change the time zone;"
+                Slog.d(LOG_TAG, "No need to change the time zone;"
                         + " device is already set to the suggested zone."
-                        + " timeZoneSuggestion=" + suggestion);
+                        + " origin=" + origin
+                        + ", newZoneId=" + newZoneId
+                        + ", cause=" + cause);
             }
             return;
         }
 
-        String msg = "Changing device time zone. currentZoneId=" + currentZoneId
-                + ", timeZoneSuggestion=" + suggestion;
+        mCallback.setDeviceTimeZone(newZoneId, sendNetworkBroadcast);
+        String msg = "Set device time zone."
+                + " origin=" + origin
+                + ", currentZoneId=" + currentZoneId
+                + ", newZoneId=" + newZoneId
+                + ", sendNetworkBroadcast" + sendNetworkBroadcast
+                + ", cause=" + cause;
         if (DBG) {
             Slog.d(LOG_TAG, msg);
         }
         mTimeZoneChangesLog.log(msg);
-        mCallback.setDeviceTimeZone(newZoneId);
+    }
+
+    private static boolean isOriginAutomatic(@Origin int origin) {
+        return origin == ORIGIN_PHONE;
     }
 
     @GuardedBy("this")
     @Nullable
-    private QualifiedPhoneTimeZoneSuggestion findBestSuggestion() {
+    private QualifiedPhoneTimeZoneSuggestion findBestPhoneSuggestion() {
         QualifiedPhoneTimeZoneSuggestion bestSuggestion = null;
 
         // Iterate over the latest QualifiedPhoneTimeZoneSuggestion objects received for each phone
@@ -376,38 +429,44 @@
     }
 
     /**
-     * Returns the current best suggestion. Not intended for general use: it is used during tests
-     * to check service behavior.
+     * Returns the current best phone suggestion. Not intended for general use: it is used during
+     * tests to check strategy behavior.
      */
     @VisibleForTesting
     @Nullable
-    public synchronized QualifiedPhoneTimeZoneSuggestion findBestSuggestionForTests() {
-        return findBestSuggestion();
+    public synchronized QualifiedPhoneTimeZoneSuggestion findBestPhoneSuggestionForTests() {
+        return findBestPhoneSuggestion();
     }
 
     /**
-     * Called when the has been a change to the automatic time zone detection setting.
+     * Called when there has been a change to the automatic time zone detection setting.
      */
     @VisibleForTesting
-    public synchronized void handleTimeZoneDetectionChange() {
+    public synchronized void handleAutoTimeZoneDetectionChange() {
         if (DBG) {
             Slog.d(LOG_TAG, "handleTimeZoneDetectionChange() called");
         }
-        if (mCallback.isTimeZoneDetectionEnabled()) {
+        if (mCallback.isAutoTimeZoneDetectionEnabled()) {
             // When the user enabled time zone detection, run the time zone detection and change the
             // device time zone if possible.
-            doTimeZoneDetection();
+            String reason = "Auto time zone detection setting enabled.";
+            doAutoTimeZoneDetection(reason);
         }
     }
 
     /**
-     * Dumps any logs held to the supplied writer.
+     * Dumps internal state such as field values.
      */
-    public synchronized void dumpLogs(IndentingPrintWriter ipw) {
-        ipw.println("TimeZoneDetectorStrategy:");
+    public synchronized void dumpState(PrintWriter pw, String[] args) {
+        pw.println("TimeZoneDetectorStrategy:");
+        pw.println("mCallback.isTimeZoneDetectionEnabled()="
+                + mCallback.isAutoTimeZoneDetectionEnabled());
+        pw.println("mCallback.isDeviceTimeZoneInitialized()="
+                + mCallback.isDeviceTimeZoneInitialized());
+        pw.println("mCallback.getDeviceTimeZone()="
+                + mCallback.getDeviceTimeZone());
 
-        ipw.increaseIndent(); // level 1
-
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
         ipw.println("Time zone change log:");
         ipw.increaseIndent(); // level 2
         mTimeZoneChangesLog.dump(ipw);
@@ -427,24 +486,13 @@
         }
         ipw.decreaseIndent(); // level 2
         ipw.decreaseIndent(); // level 1
-    }
+        ipw.flush();
 
-    /**
-     * Dumps internal state such as field values.
-     */
-    public synchronized void dumpState(PrintWriter pw) {
-        pw.println("mCurrentSuggestion=" + mCurrentSuggestion);
-        pw.println("mCallback.isTimeZoneDetectionEnabled()="
-                + mCallback.isTimeZoneDetectionEnabled());
-        pw.println("mCallback.isDeviceTimeZoneInitialized()="
-                + mCallback.isDeviceTimeZoneInitialized());
-        pw.println("mCallback.getDeviceTimeZone()="
-                + mCallback.getDeviceTimeZone());
         pw.flush();
     }
 
     /**
-     * A method used to inspect service state during tests. Not intended for general use.
+     * A method used to inspect strategy state during tests. Not intended for general use.
      */
     @VisibleForTesting
     public synchronized QualifiedPhoneTimeZoneSuggestion getLatestPhoneSuggestion(int phoneId) {
diff --git a/services/core/java/com/android/server/wm/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/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java
rename to services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java
index 317fd4d..8a7edf7 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java
@@ -40,7 +40,7 @@
 import java.time.Duration;
 
 @RunWith(AndroidJUnit4.class)
-public class SimpleTimeZoneDetectorStrategyTest {
+public class SimpleTimeDetectorStrategyTest {
 
     private static final Scenario SCENARIO_1 = new Scenario.Builder()
             .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
@@ -440,7 +440,7 @@
             mSystemClockMillis = systemClockMillis;
         }
 
-        public void pokeTimeDetectionEnabled(boolean enabled) {
+        public void pokeAutoTimeDetectionEnabled(boolean enabled) {
             mTimeDetectionEnabled = enabled;
         }
 
@@ -457,6 +457,10 @@
             mSystemClockMillis += incrementMillis;
         }
 
+        public void simulateAutoTimeZoneDetectionToggle() {
+            mTimeDetectionEnabled = !mTimeDetectionEnabled;
+        }
+
         public void verifySystemClockNotSet() {
             assertFalse(mSystemClockWasSet);
         }
@@ -493,7 +497,7 @@
         private final FakeCallback mFakeCallback;
         private final SimpleTimeDetectorStrategy mSimpleTimeDetectorStrategy;
 
-        public Script() {
+        Script() {
             mFakeCallback = new FakeCallback();
             mSimpleTimeDetectorStrategy = new SimpleTimeDetectorStrategy();
             mSimpleTimeDetectorStrategy.initialize(mFakeCallback);
@@ -501,7 +505,7 @@
         }
 
         Script pokeTimeDetectionEnabled(boolean enabled) {
-            mFakeCallback.pokeTimeDetectionEnabled(enabled);
+            mFakeCallback.pokeAutoTimeDetectionEnabled(enabled);
             return this;
         }
 
@@ -535,9 +539,8 @@
         }
 
         Script simulateAutoTimeDetectionToggle() {
-            boolean enabled = !mFakeCallback.isAutoTimeDetectionEnabled();
-            mFakeCallback.pokeTimeDetectionEnabled(enabled);
-            mSimpleTimeDetectorStrategy.handleAutoTimeDetectionToggle(enabled);
+            mFakeCallback.simulateAutoTimeZoneDetectionToggle();
+            mSimpleTimeDetectorStrategy.handleAutoTimeDetectionChanged();
             return this;
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 4efe771..9951e85 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -17,13 +17,11 @@
 package com.android.server.timedetector;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -32,12 +30,17 @@
 import android.app.timedetector.PhoneTimeSuggestion;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
 import android.util.TimestampedValue;
 
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.timedetector.TimeDetectorStrategy.Callback;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -52,54 +55,62 @@
     private Callback mMockCallback;
 
     private TimeDetectorService mTimeDetectorService;
+    private HandlerThread mHandlerThread;
+    private TestHandler mTestHandler;
+
 
     @Before
     public void setUp() {
         mMockContext = mock(Context.class);
+
+        // Create a thread + handler for processing the work that the service posts.
+        mHandlerThread = new HandlerThread("TimeDetectorServiceTest");
+        mHandlerThread.start();
+        mTestHandler = new TestHandler(mHandlerThread.getLooper());
+
         mMockCallback = mock(Callback.class);
         mStubbedTimeDetectorStrategy = new StubbedTimeDetectorStrategy();
 
         mTimeDetectorService = new TimeDetectorService(
-                mMockContext, mMockCallback,
+                mMockContext, mTestHandler, mMockCallback,
                 mStubbedTimeDetectorStrategy);
     }
 
-    @Test(expected=SecurityException.class)
-    public void testStubbedCall_withoutPermission() {
-        doThrow(new SecurityException("Mock"))
-                .when(mMockContext).enforceCallingPermission(anyString(), any());
-        PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion();
-
-        try {
-            mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion);
-        } finally {
-            verify(mMockContext).enforceCallingPermission(
-                    eq(android.Manifest.permission.SET_TIME), anyString());
-        }
+    @After
+    public void tearDown() throws Exception {
+        mHandlerThread.quit();
+        mHandlerThread.join();
     }
 
     @Test
-    public void testSuggestPhoneTime() {
+    public void testSuggestPhoneTime() throws Exception {
         doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
 
         PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion();
         mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion);
-
-        verify(mMockContext)
-                .enforceCallingPermission(eq(android.Manifest.permission.SET_TIME), anyString());
-        mStubbedTimeDetectorStrategy.verifySuggestPhoneTimeCalled(phoneTimeSuggestion);
-    }
-
-    @Test
-    public void testSuggestManualTime() {
-        doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
-
-        ManualTimeSuggestion manualTimeSuggestion = createManualTimeSuggestion();
-        mTimeDetectorService.suggestManualTime(manualTimeSuggestion);
+        mTestHandler.assertTotalMessagesEnqueued(1);
 
         verify(mMockContext).enforceCallingPermission(
                 eq(android.Manifest.permission.SET_TIME),
                 anyString());
+
+        mTestHandler.waitForEmptyQueue();
+        mStubbedTimeDetectorStrategy.verifySuggestPhoneTimeCalled(phoneTimeSuggestion);
+    }
+
+    @Test
+    public void testSuggestManualTime() throws Exception {
+        doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
+
+        ManualTimeSuggestion manualTimeSuggestion = createManualTimeSuggestion();
+        mTimeDetectorService.suggestManualTime(manualTimeSuggestion);
+        mTestHandler.assertTotalMessagesEnqueued(1);
+
+        verify(mMockContext).enforceCallingPermission(
+                eq(android.Manifest.permission.SET_TIME),
+                anyString());
+
+        mTestHandler.waitForEmptyQueue();
         mStubbedTimeDetectorStrategy.verifySuggestManualTimeCalled(manualTimeSuggestion);
     }
 
@@ -115,18 +126,16 @@
     }
 
     @Test
-    public void testAutoTimeDetectionToggle() {
-        when(mMockCallback.isAutoTimeDetectionEnabled()).thenReturn(true);
+    public void testAutoTimeDetectionToggle() throws Exception {
+        mTimeDetectorService.handleAutoTimeDetectionToggle();
+        mTestHandler.assertTotalMessagesEnqueued(1);
+        mTestHandler.waitForEmptyQueue();
+        mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled();
 
         mTimeDetectorService.handleAutoTimeDetectionToggle();
-
-        mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(true);
-
-        when(mMockCallback.isAutoTimeDetectionEnabled()).thenReturn(false);
-
-        mTimeDetectorService.handleAutoTimeDetectionToggle();
-
-        mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(false);
+        mTestHandler.assertTotalMessagesEnqueued(2);
+        mTestHandler.waitForEmptyQueue();
+        mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled();
     }
 
     private static PhoneTimeSuggestion createPhoneTimeSuggestion() {
@@ -147,7 +156,7 @@
         // Call tracking.
         private PhoneTimeSuggestion mLastPhoneSuggestion;
         private ManualTimeSuggestion mLastManualSuggestion;
-        private Boolean mLastAutoTimeDetectionToggle;
+        private boolean mLastAutoTimeDetectionToggleCalled;
         private boolean mDumpCalled;
 
         @Override
@@ -167,9 +176,9 @@
         }
 
         @Override
-        public void handleAutoTimeDetectionToggle(boolean enabled) {
+        public void handleAutoTimeDetectionChanged() {
             resetCallTracking();
-            mLastAutoTimeDetectionToggle = enabled;
+            mLastAutoTimeDetectionToggleCalled = true;
         }
 
         @Override
@@ -181,7 +190,7 @@
         void resetCallTracking() {
             mLastPhoneSuggestion = null;
             mLastManualSuggestion = null;
-            mLastAutoTimeDetectionToggle = null;
+            mLastAutoTimeDetectionToggleCalled = false;
             mDumpCalled = false;
         }
 
@@ -193,13 +202,45 @@
             assertEquals(expectedSuggestion, mLastManualSuggestion);
         }
 
-        void verifyHandleAutoTimeDetectionToggleCalled(boolean expectedEnable) {
-            assertNotNull(mLastAutoTimeDetectionToggle);
-            assertEquals(expectedEnable, mLastAutoTimeDetectionToggle);
+        void verifyHandleAutoTimeDetectionToggleCalled() {
+            assertTrue(mLastAutoTimeDetectionToggleCalled);
         }
 
         void verifyDumpCalled() {
             assertTrue(mDumpCalled);
         }
     }
+
+    /**
+     * A Handler that can track posts/sends and wait for work to be completed.
+     */
+    private static class TestHandler extends Handler {
+
+        private int mMessagesSent;
+
+        TestHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+            mMessagesSent++;
+            return super.sendMessageAtTime(msg, uptimeMillis);
+        }
+
+        /** Asserts the number of messages posted or sent is as expected. */
+        void assertTotalMessagesEnqueued(int expected) {
+            assertEquals(expected, mMessagesSent);
+        }
+
+        /**
+         * Waits for all currently enqueued work due to be processed to be completed before
+         * returning.
+         */
+        void waitForEmptyQueue() throws InterruptedException {
+            while (!getLooper().getQueue().isIdle()) {
+                Thread.sleep(100);
+            }
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java
index f9f23c3..270436d 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java
@@ -24,18 +24,19 @@
 import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
 import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
 
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.SCORE_HIGH;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.SCORE_HIGHEST;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.SCORE_LOW;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.SCORE_MEDIUM;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.SCORE_NONE;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.SCORE_USAGE_THRESHOLD;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_HIGH;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_HIGHEST;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_LOW;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_MEDIUM;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_NONE;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_USAGE_THRESHOLD;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
 import android.app.timezonedetector.PhoneTimeZoneSuggestion;
 import android.app.timezonedetector.PhoneTimeZoneSuggestion.MatchType;
 import android.app.timezonedetector.PhoneTimeZoneSuggestion.Quality;
@@ -49,6 +50,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.LinkedList;
+import java.util.Objects;
 
 /**
  * White-box unit tests for {@link TimeZoneDetectorStrategy}.
@@ -64,16 +66,17 @@
     // than the previous.
     private static final SuggestionTestCase[] TEST_CASES = new SuggestionTestCase[] {
             newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
-                    QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, SCORE_LOW),
+                    QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, PHONE_SCORE_LOW),
             newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET,
-                    SCORE_MEDIUM),
+                    PHONE_SCORE_MEDIUM),
             newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET,
-                    QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, SCORE_MEDIUM),
-            newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, QUALITY_SINGLE_ZONE, SCORE_HIGH),
-            newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE, SCORE_HIGH),
+                    QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, PHONE_SCORE_MEDIUM),
+            newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, QUALITY_SINGLE_ZONE, PHONE_SCORE_HIGH),
+            newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE,
+                    PHONE_SCORE_HIGH),
             newTestCase(MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY,
-                    QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, SCORE_HIGHEST),
-            newTestCase(MATCH_TYPE_EMULATOR_ZONE_ID, QUALITY_SINGLE_ZONE, SCORE_HIGHEST),
+                    QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, PHONE_SCORE_HIGHEST),
+            newTestCase(MATCH_TYPE_EMULATOR_ZONE_ID, QUALITY_SINGLE_ZONE, PHONE_SCORE_HIGHEST),
     };
 
     private TimeZoneDetectorStrategy mTimeZoneDetectorStrategy;
@@ -87,11 +90,11 @@
     }
 
     @Test
-    public void testEmptySuggestions() {
+    public void testEmptyPhoneSuggestions() {
         PhoneTimeZoneSuggestion phone1TimeZoneSuggestion = createEmptyPhone1Suggestion();
         PhoneTimeZoneSuggestion phone2TimeZoneSuggestion = createEmptyPhone2Suggestion();
         Script script = new Script()
-                .initializeTimeZoneDetectionEnabled(true)
+                .initializeAutoTimeZoneDetection(true)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         script.suggestPhoneTimeZone(phone1TimeZoneSuggestion)
@@ -99,38 +102,38 @@
 
         // Assert internal service state.
         QualifiedPhoneTimeZoneSuggestion expectedPhone1ScoredSuggestion =
-                new QualifiedPhoneTimeZoneSuggestion(phone1TimeZoneSuggestion, SCORE_NONE);
+                new QualifiedPhoneTimeZoneSuggestion(phone1TimeZoneSuggestion, PHONE_SCORE_NONE);
         assertEquals(expectedPhone1ScoredSuggestion,
                 mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
         assertNull(mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
         assertEquals(expectedPhone1ScoredSuggestion,
-                mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
 
         script.suggestPhoneTimeZone(phone2TimeZoneSuggestion)
                 .verifyTimeZoneNotSet();
 
         // Assert internal service state.
         QualifiedPhoneTimeZoneSuggestion expectedPhone2ScoredSuggestion =
-                new QualifiedPhoneTimeZoneSuggestion(phone2TimeZoneSuggestion, SCORE_NONE);
+                new QualifiedPhoneTimeZoneSuggestion(phone2TimeZoneSuggestion, PHONE_SCORE_NONE);
         assertEquals(expectedPhone1ScoredSuggestion,
                 mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
         assertEquals(expectedPhone2ScoredSuggestion,
                 mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
         // Phone 1 should always beat phone 2, all other things being equal.
         assertEquals(expectedPhone1ScoredSuggestion,
-                mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
     }
 
     @Test
-    public void testFirstPlausibleSuggestionAcceptedWhenTimeZoneUninitialized() {
+    public void testFirstPlausiblePhoneSuggestionAcceptedWhenTimeZoneUninitialized() {
         SuggestionTestCase testCase = newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
-                QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, SCORE_LOW);
+                QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, PHONE_SCORE_LOW);
         PhoneTimeZoneSuggestion lowQualitySuggestion =
                 testCase.createSuggestion(PHONE1_ID, "America/New_York");
 
         // The device time zone setting is left uninitialized.
         Script script = new Script()
-                .initializeTimeZoneDetectionEnabled(true);
+                .initializeAutoTimeZoneDetection(true);
 
         // The very first suggestion will be taken.
         script.suggestPhoneTimeZone(lowQualitySuggestion)
@@ -142,7 +145,7 @@
         assertEquals(expectedScoredSuggestion,
                 mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
         assertEquals(expectedScoredSuggestion,
-                mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
 
         // Another low quality suggestion will be ignored now that the setting is initialized.
         PhoneTimeZoneSuggestion lowQualitySuggestion2 =
@@ -156,7 +159,7 @@
         assertEquals(expectedScoredSuggestion2,
                 mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
         assertEquals(expectedScoredSuggestion2,
-                mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
     }
 
     /**
@@ -164,12 +167,12 @@
      * the strategy is "opinionated".
      */
     @Test
-    public void testTogglingTimeZoneDetection() {
+    public void testTogglingAutoTimeZoneDetection() {
         Script script = new Script();
 
         for (SuggestionTestCase testCase : TEST_CASES) {
             // Start with the device in a known state.
-            script.initializeTimeZoneDetectionEnabled(false)
+            script.initializeAutoTimeZoneDetection(false)
                     .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
             PhoneTimeZoneSuggestion suggestion =
@@ -186,14 +189,14 @@
             assertEquals(expectedScoredSuggestion,
                     mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
             assertEquals(expectedScoredSuggestion,
-                    mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                    mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
 
             // Toggling the time zone setting on should cause the device setting to be set.
-            script.timeZoneDetectionEnabled(true);
+            script.autoTimeZoneDetectionEnabled(true);
 
             // When time zone detection is already enabled the suggestion (if it scores highly
             // enough) should be set immediately.
-            if (testCase.expectedScore >= SCORE_USAGE_THRESHOLD) {
+            if (testCase.expectedScore >= PHONE_SCORE_USAGE_THRESHOLD) {
                 script.verifyTimeZoneSetAndReset(suggestion);
             } else {
                 script.verifyTimeZoneNotSet();
@@ -203,24 +206,24 @@
             assertEquals(expectedScoredSuggestion,
                     mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
             assertEquals(expectedScoredSuggestion,
-                    mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                    mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
 
             // Toggling the time zone setting should off should do nothing.
-            script.timeZoneDetectionEnabled(false)
+            script.autoTimeZoneDetectionEnabled(false)
                     .verifyTimeZoneNotSet();
 
             // Assert internal service state.
             assertEquals(expectedScoredSuggestion,
                     mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
             assertEquals(expectedScoredSuggestion,
-                    mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                    mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
         }
     }
 
     @Test
-    public void testSuggestionsSinglePhone() {
+    public void testPhoneSuggestionsSinglePhone() {
         Script script = new Script()
-                .initializeTimeZoneDetectionEnabled(true)
+                .initializeAutoTimeZoneDetection(true)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         for (SuggestionTestCase testCase : TEST_CASES) {
@@ -254,7 +257,7 @@
                 new QualifiedPhoneTimeZoneSuggestion(zonePhone1Suggestion, testCase.expectedScore);
 
         script.suggestPhoneTimeZone(zonePhone1Suggestion);
-        if (testCase.expectedScore >= SCORE_USAGE_THRESHOLD) {
+        if (testCase.expectedScore >= PHONE_SCORE_USAGE_THRESHOLD) {
             script.verifyTimeZoneSetAndReset(zonePhone1Suggestion);
         } else {
             script.verifyTimeZoneNotSet();
@@ -264,7 +267,7 @@
         assertEquals(expectedZonePhone1ScoredSuggestion,
                 mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
         assertEquals(expectedZonePhone1ScoredSuggestion,
-                mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
     }
 
     /**
@@ -278,12 +281,12 @@
         PhoneTimeZoneSuggestion emptyPhone1Suggestion = createEmptyPhone1Suggestion();
         PhoneTimeZoneSuggestion emptyPhone2Suggestion = createEmptyPhone2Suggestion();
         QualifiedPhoneTimeZoneSuggestion expectedEmptyPhone1ScoredSuggestion =
-                new QualifiedPhoneTimeZoneSuggestion(emptyPhone1Suggestion, SCORE_NONE);
+                new QualifiedPhoneTimeZoneSuggestion(emptyPhone1Suggestion, PHONE_SCORE_NONE);
         QualifiedPhoneTimeZoneSuggestion expectedEmptyPhone2ScoredSuggestion =
-                new QualifiedPhoneTimeZoneSuggestion(emptyPhone2Suggestion, SCORE_NONE);
+                new QualifiedPhoneTimeZoneSuggestion(emptyPhone2Suggestion, PHONE_SCORE_NONE);
 
         Script script = new Script()
-                .initializeTimeZoneDetectionEnabled(true)
+                .initializeAutoTimeZoneDetection(true)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
                 // Initialize the latest suggestions as empty so we don't need to worry about nulls
                 // below for the first loop.
@@ -305,7 +308,7 @@
 
             // Start the test by making a suggestion for phone 1.
             script.suggestPhoneTimeZone(zonePhone1Suggestion);
-            if (testCase.expectedScore >= SCORE_USAGE_THRESHOLD) {
+            if (testCase.expectedScore >= PHONE_SCORE_USAGE_THRESHOLD) {
                 script.verifyTimeZoneSetAndReset(zonePhone1Suggestion);
             } else {
                 script.verifyTimeZoneNotSet();
@@ -317,7 +320,7 @@
             assertEquals(expectedEmptyPhone2ScoredSuggestion,
                     mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
             assertEquals(expectedZonePhone1ScoredSuggestion,
-                    mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                    mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
 
             // Phone 2 then makes an alternative suggestion with an identical score. Phone 1's
             // suggestion should still "win" if it is above the required threshold.
@@ -331,13 +334,13 @@
                     mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
             // Phone 1 should always beat phone 2, all other things being equal.
             assertEquals(expectedZonePhone1ScoredSuggestion,
-                    mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                    mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
 
             // Withdrawing phone 1's suggestion should leave phone 2 as the new winner. Since the
             // zoneId is different, the time zone setting should be updated if the score is high
             // enough.
             script.suggestPhoneTimeZone(emptyPhone1Suggestion);
-            if (testCase.expectedScore >= SCORE_USAGE_THRESHOLD) {
+            if (testCase.expectedScore >= PHONE_SCORE_USAGE_THRESHOLD) {
                 script.verifyTimeZoneSetAndReset(zonePhone2Suggestion);
             } else {
                 script.verifyTimeZoneNotSet();
@@ -349,7 +352,7 @@
             assertEquals(expectedZonePhone2ScoredSuggestion,
                     mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
             assertEquals(expectedZonePhone2ScoredSuggestion,
-                    mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+                    mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests());
 
             // Reset the state for the next loop.
             script.suggestPhoneTimeZone(emptyPhone2Suggestion)
@@ -369,10 +372,11 @@
     @Test
     public void testTimeZoneDetectorStrategyDoesNotAssumeCurrentSetting() {
         Script script = new Script()
-                .initializeTimeZoneDetectionEnabled(true);
+                .initializeAutoTimeZoneDetection(true);
 
         SuggestionTestCase testCase =
-                newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE, SCORE_HIGH);
+                newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE,
+                        PHONE_SCORE_HIGH);
         PhoneTimeZoneSuggestion losAngelesSuggestion =
                 testCase.createSuggestion(PHONE1_ID, "America/Los_Angeles");
         PhoneTimeZoneSuggestion newYorkSuggestion =
@@ -387,21 +391,49 @@
 
         // Toggling time zone detection should set the device time zone only if the current setting
         // value is different from the most recent phone suggestion.
-        script.timeZoneDetectionEnabled(false)
+        script.autoTimeZoneDetectionEnabled(false)
                 .verifyTimeZoneNotSet()
-                .timeZoneDetectionEnabled(true)
+                .autoTimeZoneDetectionEnabled(true)
                 .verifyTimeZoneNotSet();
 
         // Simulate a user turning auto detection off, a new suggestion being made while auto
         // detection is off, and the user turning it on again.
-        script.timeZoneDetectionEnabled(false)
+        script.autoTimeZoneDetectionEnabled(false)
                 .suggestPhoneTimeZone(newYorkSuggestion)
                 .verifyTimeZoneNotSet();
         // Latest suggestion should be used.
-        script.timeZoneDetectionEnabled(true)
+        script.autoTimeZoneDetectionEnabled(true)
                 .verifyTimeZoneSetAndReset(newYorkSuggestion);
     }
 
+    @Test
+    public void testManualSuggestion_autoTimeZoneDetectionEnabled() {
+        Script script = new Script()
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .initializeAutoTimeZoneDetection(true);
+
+        // Auto time zone detection is enabled so the manual suggestion should be ignored.
+        script.suggestManualTimeZone(createManualSuggestion("Europe/Paris"))
+            .verifyTimeZoneNotSet();
+    }
+
+
+    @Test
+    public void testManualSuggestion_autoTimeZoneDetectionDisabled() {
+        Script script = new Script()
+                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+                .initializeAutoTimeZoneDetection(false);
+
+        // Auto time zone detection is disabled so the manual suggestion should be used.
+        ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris");
+        script.suggestManualTimeZone(manualSuggestion)
+            .verifyTimeZoneSetAndReset(manualSuggestion);
+    }
+
+    private ManualTimeZoneSuggestion createManualSuggestion(String zoneId) {
+        return new ManualTimeZoneSuggestion(zoneId);
+    }
+
     private static PhoneTimeZoneSuggestion createEmptyPhone1Suggestion() {
         return new PhoneTimeZoneSuggestion.Builder(PHONE1_ID).build();
     }
@@ -410,55 +442,86 @@
         return new PhoneTimeZoneSuggestion.Builder(PHONE2_ID).build();
     }
 
-    class FakeTimeZoneDetectorStrategyCallback implements TimeZoneDetectorStrategy.Callback {
+    static class FakeTimeZoneDetectorStrategyCallback implements TimeZoneDetectorStrategy.Callback {
 
-        private boolean mTimeZoneDetectionEnabled;
-        private TestState<String> mTimeZoneId = new TestState<>();
+        private boolean mAutoTimeZoneDetectionEnabled;
+        private TestState<TimeZoneChange> mTimeZoneChanges = new TestState<>();
+        private String mTimeZoneId;
 
         @Override
-        public boolean isTimeZoneDetectionEnabled() {
-            return mTimeZoneDetectionEnabled;
+        public boolean isAutoTimeZoneDetectionEnabled() {
+            return mAutoTimeZoneDetectionEnabled;
         }
 
         @Override
         public boolean isDeviceTimeZoneInitialized() {
-            return mTimeZoneId.getLatest() != null;
+            return mTimeZoneId != null;
         }
 
         @Override
         public String getDeviceTimeZone() {
-            return mTimeZoneId.getLatest();
+            return mTimeZoneId;
         }
 
         @Override
-        public void setDeviceTimeZone(String zoneId) {
-            mTimeZoneId.set(zoneId);
+        public void setDeviceTimeZone(String zoneId, boolean withNetworkBroadcast) {
+            mTimeZoneId = zoneId;
+            mTimeZoneChanges.set(new TimeZoneChange(zoneId, withNetworkBroadcast));
         }
 
-        void initializeTimeZoneDetectionEnabled(boolean enabled) {
-            mTimeZoneDetectionEnabled = enabled;
+        void initializeAutoTimeZoneDetection(boolean enabled) {
+            mAutoTimeZoneDetectionEnabled = enabled;
         }
 
         void initializeTimeZone(String zoneId) {
-            mTimeZoneId.init(zoneId);
+            mTimeZoneId = zoneId;
         }
 
-        void setTimeZoneDetectionEnabled(boolean enabled) {
-            mTimeZoneDetectionEnabled = enabled;
+        void setAutoTimeZoneDetectionEnabled(boolean enabled) {
+            mAutoTimeZoneDetectionEnabled = enabled;
         }
 
         void assertTimeZoneNotSet() {
-            mTimeZoneId.assertHasNotBeenSet();
+            mTimeZoneChanges.assertHasNotBeenSet();
         }
 
-        void assertTimeZoneSet(String timeZoneId) {
-            mTimeZoneId.assertHasBeenSet();
-            mTimeZoneId.assertChangeCount(1);
-            mTimeZoneId.assertLatestEquals(timeZoneId);
+        void assertTimeZoneSet(String timeZoneId, boolean withNetworkBroadcast) {
+            mTimeZoneChanges.assertHasBeenSet();
+            mTimeZoneChanges.assertChangeCount(1);
+            TimeZoneChange expectedChange = new TimeZoneChange(timeZoneId, withNetworkBroadcast);
+            mTimeZoneChanges.assertLatestEquals(expectedChange);
         }
 
         void commitAllChanges() {
-            mTimeZoneId.commitLatest();
+            mTimeZoneChanges.commitLatest();
+        }
+    }
+
+    private static class TimeZoneChange {
+        private final String mTimeZoneId;
+        private final boolean mWithNetworkBroadcast;
+
+        private TimeZoneChange(String timeZoneId, boolean withNetworkBroadcast) {
+            mTimeZoneId = timeZoneId;
+            mWithNetworkBroadcast = withNetworkBroadcast;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            TimeZoneChange that = (TimeZoneChange) o;
+            return mWithNetworkBroadcast == that.mWithNetworkBroadcast
+                    && mTimeZoneId.equals(that.mTimeZoneId);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mTimeZoneId, mWithNetworkBroadcast);
         }
     }
 
@@ -517,8 +580,8 @@
      */
     private class Script {
 
-        Script initializeTimeZoneDetectionEnabled(boolean enabled) {
-            mFakeTimeZoneDetectorStrategyCallback.initializeTimeZoneDetectionEnabled(enabled);
+        Script initializeAutoTimeZoneDetection(boolean enabled) {
+            mFakeTimeZoneDetectorStrategyCallback.initializeAutoTimeZoneDetection(enabled);
             return this;
         }
 
@@ -527,24 +590,21 @@
             return this;
         }
 
-        Script timeZoneDetectionEnabled(boolean enabled) {
-            mFakeTimeZoneDetectorStrategyCallback.setTimeZoneDetectionEnabled(enabled);
-            mTimeZoneDetectorStrategy.handleTimeZoneDetectionChange();
+        Script autoTimeZoneDetectionEnabled(boolean enabled) {
+            mFakeTimeZoneDetectorStrategyCallback.setAutoTimeZoneDetectionEnabled(enabled);
+            mTimeZoneDetectorStrategy.handleAutoTimeZoneDetectionChange();
             return this;
         }
 
-        /** Simulates the time zone detection service receiving a phone-originated suggestion. */
+        /** Simulates the time zone detection strategy receiving a phone-originated suggestion. */
         Script suggestPhoneTimeZone(PhoneTimeZoneSuggestion phoneTimeZoneSuggestion) {
             mTimeZoneDetectorStrategy.suggestPhoneTimeZone(phoneTimeZoneSuggestion);
             return this;
         }
 
-        /** Simulates the user manually setting the time zone. */
-        Script manuallySetTimeZone(String timeZoneId) {
-            // Assert the test code is correct to call this method.
-            assertFalse(mFakeTimeZoneDetectorStrategyCallback.isTimeZoneDetectionEnabled());
-
-            mFakeTimeZoneDetectorStrategyCallback.initializeTimeZone(timeZoneId);
+        /** Simulates the time zone detection strategy receiving a user-originated suggestion. */
+        Script suggestManualTimeZone(ManualTimeZoneSuggestion manualTimeZoneSuggestion) {
+            mTimeZoneDetectorStrategy.suggestManualTimeZone(manualTimeZoneSuggestion);
             return this;
         }
 
@@ -553,8 +613,22 @@
             return this;
         }
 
-        Script verifyTimeZoneSetAndReset(PhoneTimeZoneSuggestion timeZoneSuggestion) {
-            mFakeTimeZoneDetectorStrategyCallback.assertTimeZoneSet(timeZoneSuggestion.getZoneId());
+        Script verifyTimeZoneSetAndReset(PhoneTimeZoneSuggestion suggestion) {
+            // Phone suggestions should cause a TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE
+            // broadcast.
+            boolean withNetworkBroadcast = true;
+            mFakeTimeZoneDetectorStrategyCallback.assertTimeZoneSet(
+                    suggestion.getZoneId(), withNetworkBroadcast);
+            mFakeTimeZoneDetectorStrategyCallback.commitAllChanges();
+            return this;
+        }
+
+        Script verifyTimeZoneSetAndReset(ManualTimeZoneSuggestion suggestion) {
+            // Manual suggestions should not cause a TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE
+            // broadcast.
+            boolean withNetworkBroadcast = false;
+            mFakeTimeZoneDetectorStrategyCallback.assertTimeZoneSet(
+                    suggestion.getZoneId(), withNetworkBroadcast);
             mFakeTimeZoneDetectorStrategyCallback.commitAllChanges();
             return this;
         }
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/ConnTestApp/AndroidManifest.xml
index 909e9bb..201cd05 100644
--- a/services/tests/servicestests/test-apps/ConnTestApp/AndroidManifest.xml
+++ b/services/tests/servicestests/test-apps/ConnTestApp/AndroidManifest.xml
@@ -18,8 +18,7 @@
         package="com.android.servicestests.apps.conntestapp">
 
     <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
-    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+    <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
 
     <application>
         <activity android:name=".ConnTestActivity"
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 734761f..12074dc3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.content.ComponentName.createRelative;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -34,7 +35,7 @@
 import android.content.Intent;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
-import android.util.SparseIntArray;
+import android.util.ArrayMap;
 
 import androidx.test.filters.SmallTest;
 
@@ -58,11 +59,13 @@
 @Presubmit
 public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
     private ActivityMetricsLogger mActivityMetricsLogger;
+    private ActivityMetricsLogger.LaunchingState mLaunchingState;
     private ActivityMetricsLaunchObserver mLaunchObserver;
     private ActivityMetricsLaunchObserverRegistry mLaunchObserverRegistry;
 
     private ActivityRecord mTrampolineActivity;
     private ActivityRecord mTopActivity;
+    private boolean mLaunchTopByTrampoline;
 
     @Before
     public void setUpAMLO() {
@@ -76,9 +79,13 @@
 
         // Sometimes we need an ActivityRecord for ActivityMetricsLogger to do anything useful.
         // This seems to be the easiest way to create an ActivityRecord.
-        mTrampolineActivity = new ActivityBuilder(mService).setCreateTask(true).build();
+        mTrampolineActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .setComponent(createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, "TrampolineActivity"))
+                .build();
         mTopActivity = new ActivityBuilder(mService)
                 .setTask(mTrampolineActivity.getTask())
+                .setComponent(createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, "TopActivity"))
                 .build();
     }
 
@@ -114,42 +121,42 @@
         return verify(mock, timeout(TimeUnit.SECONDS.toMillis(5)));
     }
 
-    private void onIntentStarted() {
-        Intent intent = new Intent("action 1");
+    private void onIntentStarted(Intent intent) {
+        notifyActivityLaunching(intent);
 
-        mActivityMetricsLogger.notifyActivityLaunching(intent);
-
-        verifyAsync(mLaunchObserver).onIntentStarted(eq(intent), anyLong());
+        // If it is launching top activity from trampoline activity, the observer shouldn't receive
+        // onActivityLaunched because the activities should belong to the same transition.
+        if (!mLaunchTopByTrampoline) {
+            verifyAsync(mLaunchObserver).onIntentStarted(eq(intent), anyLong());
+        }
         verifyNoMoreInteractions(mLaunchObserver);
     }
 
     @Test
     public void testOnIntentFailed() {
-        onIntentStarted();
+        onIntentStarted(new Intent("testOnIntentFailed"));
 
         // Bringing an intent that's already running 'to front' is not considered
         // as an ACTIVITY_LAUNCHED state transition.
-        mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
-                null /* launchedActivity */);
+        notifyActivityLaunched(START_TASK_TO_FRONT, null /* launchedActivity */);
 
         verifyAsync(mLaunchObserver).onIntentFailed();
         verifyNoMoreInteractions(mLaunchObserver);
     }
 
-    private void onActivityLaunched() {
-        onIntentStarted();
+    private void onActivityLaunched(ActivityRecord activity) {
+        onIntentStarted(activity.intent);
+        notifyActivityLaunched(START_SUCCESS, activity);
 
-        mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTopActivity);
-
-        verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mTopActivity), anyInt());
+        verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(activity), anyInt());
         verifyNoMoreInteractions(mLaunchObserver);
     }
 
     @Test
     public void testOnActivityLaunchFinished() {
-        onActivityLaunched();
+        onActivityLaunched(mTopActivity);
 
-        notifyTransitionStarting();
+        notifyTransitionStarting(mTopActivity);
         notifyWindowsDrawn(mTopActivity);
 
         verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTopActivity), anyLong());
@@ -158,12 +165,12 @@
 
     @Test
     public void testOnActivityLaunchCancelled_hasDrawn() {
-        onActivityLaunched();
+        onActivityLaunched(mTopActivity);
 
         mTopActivity.mVisibleRequested = mTopActivity.mDrawn = true;
 
         // Cannot time already-visible activities.
-        mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
+        notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
 
         verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mTopActivity));
         verifyNoMoreInteractions(mLaunchObserver);
@@ -182,8 +189,8 @@
                 .build();
         mSupervisor.readyToResume();
 
-        mActivityMetricsLogger.notifyActivityLaunching(noDrawnActivity.intent);
-        mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, noDrawnActivity);
+        notifyActivityLaunching(noDrawnActivity.intent);
+        notifyActivityLaunched(START_SUCCESS, noDrawnActivity);
 
         noDrawnActivity.destroyIfPossible("test");
         mActivityMetricsLogger.notifyVisibilityChanged(noDrawnActivity);
@@ -193,12 +200,12 @@
 
     @Test
     public void testOnReportFullyDrawn() {
-        onActivityLaunched();
+        onActivityLaunched(mTopActivity);
 
         // The activity reports fully drawn before windows drawn, then the fully drawn event will
         // be pending (see {@link WindowingModeTransitionInfo#pendingFullyDrawn}).
         mActivityMetricsLogger.logAppTransitionReportedDrawn(mTopActivity, false);
-        notifyTransitionStarting();
+        notifyTransitionStarting(mTopActivity);
         // The pending fully drawn event should send when the actual windows drawn event occurs.
         notifyWindowsDrawn(mTopActivity);
 
@@ -208,35 +215,56 @@
     }
 
     private void onActivityLaunchedTrampoline() {
-        onIntentStarted();
-
-        mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTrampolineActivity);
+        onIntentStarted(mTrampolineActivity.intent);
+        notifyActivityLaunched(START_SUCCESS, mTrampolineActivity);
 
         verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mTrampolineActivity), anyInt());
 
         // A second, distinct, activity launch is coalesced into the current app launch sequence.
-        mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTopActivity);
+        mLaunchTopByTrampoline = true;
+        onIntentStarted(mTopActivity.intent);
+        notifyActivityLaunched(START_SUCCESS, mTopActivity);
 
+        // The observer shouldn't receive onActivityLaunched for an existing transition.
         verifyNoMoreInteractions(mLaunchObserver);
     }
 
-    private void notifyTransitionStarting() {
-        mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
-                SystemClock.elapsedRealtimeNanos());
+    private void notifyActivityLaunching(Intent intent) {
+        final ActivityMetricsLogger.LaunchingState previousState = mLaunchingState;
+        mLaunchingState = mActivityMetricsLogger.notifyActivityLaunching(intent,
+                mLaunchTopByTrampoline ? mTrampolineActivity : null /* caller */);
+        if (mLaunchTopByTrampoline) {
+            // The transition of TrampolineActivity has not been completed, so when the next
+            // activity is starting from it, the same launching state should be returned.
+            assertWithMessage("Use existing launching state for a caller in active transition")
+                    .that(previousState).isEqualTo(mLaunchingState);
+        }
+    }
+
+    private void notifyActivityLaunched(int resultCode, ActivityRecord activity) {
+        mActivityMetricsLogger.notifyActivityLaunched(mLaunchingState, resultCode, activity);
+    }
+
+    private void notifyTransitionStarting(ActivityRecord activity) {
+        final ArrayMap<ActivityRecord, Integer> reasons = new ArrayMap<>();
+        reasons.put(activity, ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN);
+        mActivityMetricsLogger.notifyTransitionStarting(reasons);
     }
 
     private void notifyWindowsDrawn(ActivityRecord r) {
-        mActivityMetricsLogger.notifyWindowsDrawn(r.getWindowingMode(),
-                SystemClock.elapsedRealtimeNanos());
+        mActivityMetricsLogger.notifyWindowsDrawn(r, SystemClock.elapsedRealtimeNanos());
     }
 
     @Test
     public void testOnActivityLaunchFinishedTrampoline() {
         onActivityLaunchedTrampoline();
 
-        notifyTransitionStarting();
+        notifyTransitionStarting(mTopActivity);
         notifyWindowsDrawn(mTrampolineActivity);
 
+        assertWithMessage("Trampoline activity is drawn but the top activity is not yet")
+                .that(mLaunchingState.allDrawn()).isFalse();
+
         notifyWindowsDrawn(mTopActivity);
 
         verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTopActivity), anyLong());
@@ -244,13 +272,23 @@
     }
 
     @Test
+    public void testDoNotCountInvisibleActivityToBeDrawn() {
+        onActivityLaunchedTrampoline();
+        mTrampolineActivity.setVisibility(false);
+        notifyWindowsDrawn(mTopActivity);
+
+        assertWithMessage("Trampoline activity is invisble so there should be no undrawn windows")
+                .that(mLaunchingState.allDrawn()).isTrue();
+    }
+
+    @Test
     public void testOnActivityLaunchCancelledTrampoline() {
         onActivityLaunchedTrampoline();
 
         mTopActivity.mDrawn = true;
 
         // Cannot time already-visible activities.
-        mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
+        notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
 
         verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mTopActivity));
         verifyNoMoreInteractions(mLaunchObserver);
@@ -268,4 +306,32 @@
                 .that(activityRecordToProto(mTrampolineActivity).length)
                 .isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
     }
+
+    @Test
+    public void testConcurrentLaunches() {
+        onActivityLaunched(mTopActivity);
+        final ActivityMetricsLogger.LaunchingState previousState = mLaunchingState;
+
+        final ActivityRecord otherActivity = new ActivityBuilder(mService)
+                .setComponent(createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, "OtherActivity"))
+                .setCreateTask(true)
+                .build();
+        // Assume the calling uid is different from the uid of TopActivity, so a new launching
+        // state should be created here.
+        onActivityLaunched(otherActivity);
+
+        assertWithMessage("Different callers should get 2 indepedent launching states")
+                .that(previousState).isNotEqualTo(mLaunchingState);
+
+        notifyTransitionStarting(otherActivity);
+        notifyWindowsDrawn(otherActivity);
+
+        verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(otherActivity), anyLong());
+
+        // The first transition should still be valid.
+        notifyTransitionStarting(mTopActivity);
+        notifyWindowsDrawn(mTopActivity);
+
+        verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTopActivity), anyLong());
+    }
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 479b144..98d7661 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1042,6 +1042,9 @@
      *
      * When {@code false}, the old behavior is used, where the toggle in accessibility settings is
      * used to set the IMS stack's RTT enabled state.
+     *
+     * @deprecated -- this flag no longer does anything. Remove once the new behavior is verified.
+     *
      * @hide
      */
     public static final String KEY_IGNORE_RTT_MODE_SETTING_BOOL =
@@ -3523,7 +3526,7 @@
         sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0);
         sDefaults.putInt(KEY_CDMA_DTMF_TONE_DELAY_INT, 100);
         sDefaults.putBoolean(KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL, false);
-        sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, false);
+        sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, true);
         sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0);
         sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true);
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index 663b09a..f4b2cef 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -18,9 +18,10 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -28,12 +29,13 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * This class enables an application to get details on why a method call failed.
- *
- * @hide
+ * Provides details on why an IMS call failed. Applications can use the methods in this class to
+ * get local or network fault behind an IMS services failure. For example, if the code is
+ * CODE_CALL_BARRED, then the call was blocked by network call barring configuration and it is not
+ * the device's bug and the user can retry the call when network lift the barring.
+ * Typical use case includes call backs when IMS call state changed with this class as a param
+ * containing details on why IMS call changed state/failed.
  */
-@SystemApi
-@TestApi
 public final class ImsReasonInfo implements Parcelable {
 
     /**
@@ -1096,9 +1098,11 @@
     /**
      * Network string error messages.
      * mExtraMessage may have these values.
+     * @hide
      */
-    public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED
-            = "Forbidden. Not Authorized for Service";
+    @SystemApi
+    public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED =
+            "Forbidden. Not Authorized for Service";
 
 
     /*
@@ -1106,21 +1110,21 @@
      * This value can be referred when the code is CODE_LOCAL_CALL_CS_RETRY_REQUIRED.
      */
     /**
-     * An extra that may be populated when the {@link CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
+     * An extra that may be populated when the {@link #CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
      * been returned.
      * <p>
      * Try to connect the call using CS
      */
     public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1;
     /**
-     * An extra that may be populated when the {@link CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
+     * An extra that may be populated when the {@link #CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
      * been returned.
      * <p>
      * Try to connect the call using CS and do not notify the user.
      */
     public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2;
     /**
-     * An extra that may be populated when the {@link CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
+     * An extra that may be populated when the {@link #CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
      * been returned.
      * <p>
      * Try to connect the call using CS by using the settings.
@@ -1130,15 +1134,18 @@
 
     // For main reason code
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@code "
+            + "#getCode()}")
     public int mCode;
     // For the extra code value; it depends on the code value.
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@code "
+            + "#getExtraCode()}")
     public int mExtraCode;
     // For the additional message of the reason info.
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@code "
+            + "#getExtraMessage()}")
     public String mExtraMessage;
 
     /** @hide */
@@ -1162,7 +1169,7 @@
         mExtraMessage = null;
     }
 
-    public ImsReasonInfo(int code, int extraCode, String extraMessage) {
+    public ImsReasonInfo(@ImsCode int code, int extraCode, @Nullable String extraMessage) {
         mCode = code;
         mExtraCode = extraCode;
         mExtraMessage = extraMessage;
@@ -1186,7 +1193,7 @@
      * @return an optional OEM specified string that provides extra information about the operation
      * result.
      */
-    public String getExtraMessage() {
+    public @Nullable String getExtraMessage() {
         return mExtraMessage;
     }
 
@@ -1205,13 +1212,13 @@
     }
 
     @Override
-    public void writeToParcel(Parcel out, int flags) {
+    public void writeToParcel(@NonNull Parcel out, int flags) {
         out.writeInt(mCode);
         out.writeInt(mExtraCode);
         out.writeString(mExtraMessage);
     }
 
-    public static final @android.annotation.NonNull Creator<ImsReasonInfo> CREATOR = new Creator<ImsReasonInfo>() {
+    public static final @NonNull Creator<ImsReasonInfo> CREATOR = new Creator<ImsReasonInfo>() {
         @Override
         public ImsReasonInfo createFromParcel(Parcel in) {
             return new ImsReasonInfo(in);
diff --git a/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";
 
     /**