Merge changes from topic "b224749799" into tm-dev

* changes:
  Allow null prev/next buttons to be INVISIBLE instead of GONE
  Remove logging and nullable return type from getCustomAction
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index aad8f9d..9e13133 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1895,11 +1895,14 @@
             // The job ran past its expected run window. Have it count towards the current window
             // and schedule a new job for the next window.
             if (DEBUG) {
-                Slog.i(TAG, "Periodic job ran after its intended window.");
+                Slog.i(TAG, "Periodic job ran after its intended window by " + diffMs + " ms");
             }
             long numSkippedWindows = (diffMs / period) + 1; // +1 to include original window
-            if (period != flex && diffMs > Math.min(PERIODIC_JOB_WINDOW_BUFFER,
-                    (period - flex) / 2)) {
+            // Determine how far into a single period the job ran, and determine if it's too close
+            // to the start of the next period. If the difference between the start of the execution
+            // window and the previous execution time inside of the period is less than the
+            // threshold, then we say that the job ran too close to the next period.
+            if (period != flex && (period - flex - (diffMs % period)) <= flex / 6) {
                 if (DEBUG) {
                     Slog.d(TAG, "Custom flex job ran too close to next window.");
                 }
diff --git a/api/api.go b/api/api.go
index 94bccaa..9aac879 100644
--- a/api/api.go
+++ b/api/api.go
@@ -27,6 +27,7 @@
 const art = "art.module.public.api"
 const conscrypt = "conscrypt.module.public.api"
 const i18n = "i18n.module.public.api"
+
 var core_libraries_modules = []string{art, conscrypt, i18n}
 
 // The intention behind this soong plugin is to generate a number of "merged"
@@ -92,6 +93,8 @@
 type MergedTxtDefinition struct {
 	// "current.txt" or "removed.txt"
 	TxtFilename string
+	// Filename in the new dist dir. "android.txt" or "android-removed.txt"
+	DistFilename string
 	// The module for the non-updatable / non-module part of the api.
 	BaseTxt string
 	// The list of modules that are relevant for this merged txt.
@@ -112,7 +115,6 @@
 	if txt.Scope != "public" {
 		filename = txt.Scope + "-" + filename
 	}
-
 	props := genruleProps{}
 	props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename)
 	props.Tools = []string{"metalava"}
@@ -126,9 +128,9 @@
 			Dest:    proptools.StringPtr(filename),
 		},
 		{
-			Targets: []string{"sdk"},
+			Targets: []string{"api_txt", "sdk"},
 			Dir:     proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"),
-			Dest:    proptools.StringPtr(txt.TxtFilename),
+			Dest:    proptools.StringPtr(txt.DistFilename),
 		},
 	}
 	props.Visibility = []string{"//visibility:public"}
@@ -240,34 +242,39 @@
 	var textFiles []MergedTxtDefinition
 
 	tagSuffix := []string{".api.txt}", ".removed-api.txt}"}
+	distFilename := []string{"android.txt", "android-removed.txt"}
 	for i, f := range []string{"current.txt", "removed.txt"} {
 		textFiles = append(textFiles, MergedTxtDefinition{
-			TxtFilename: f,
-			BaseTxt:     ":non-updatable-" + f,
-			Modules:     bootclasspath,
-			ModuleTag:   "{.public" + tagSuffix[i],
-			Scope:       "public",
+			TxtFilename:  f,
+			DistFilename: distFilename[i],
+			BaseTxt:      ":non-updatable-" + f,
+			Modules:      bootclasspath,
+			ModuleTag:    "{.public" + tagSuffix[i],
+			Scope:        "public",
 		})
 		textFiles = append(textFiles, MergedTxtDefinition{
-			TxtFilename: f,
-			BaseTxt:     ":non-updatable-system-" + f,
-			Modules:     bootclasspath,
-			ModuleTag:   "{.system" + tagSuffix[i],
-			Scope:       "system",
+			TxtFilename:  f,
+			DistFilename: distFilename[i],
+			BaseTxt:      ":non-updatable-system-" + f,
+			Modules:      bootclasspath,
+			ModuleTag:    "{.system" + tagSuffix[i],
+			Scope:        "system",
 		})
 		textFiles = append(textFiles, MergedTxtDefinition{
-			TxtFilename: f,
-			BaseTxt:     ":non-updatable-module-lib-" + f,
-			Modules:     bootclasspath,
-			ModuleTag:   "{.module-lib" + tagSuffix[i],
-			Scope:       "module-lib",
+			TxtFilename:  f,
+			DistFilename: distFilename[i],
+			BaseTxt:      ":non-updatable-module-lib-" + f,
+			Modules:      bootclasspath,
+			ModuleTag:    "{.module-lib" + tagSuffix[i],
+			Scope:        "module-lib",
 		})
 		textFiles = append(textFiles, MergedTxtDefinition{
-			TxtFilename: f,
-			BaseTxt:     ":non-updatable-system-server-" + f,
-			Modules:     system_server_classpath,
-			ModuleTag:   "{.system-server" + tagSuffix[i],
-			Scope:       "system-server",
+			TxtFilename:  f,
+			DistFilename: distFilename[i],
+			BaseTxt:      ":non-updatable-system-server-" + f,
+			Modules:      system_server_classpath,
+			ModuleTag:    "{.system-server" + tagSuffix[i],
+			Scope:        "system-server",
 		})
 	}
 	for _, txt := range textFiles {
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 52f883b..d464e26 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -37,6 +37,8 @@
 import com.android.internal.telecom.ITelecomService;
 
 import java.io.PrintStream;
+import java.util.Arrays;
+import java.util.stream.Collectors;
 
 public final class Telecom extends BaseCommand {
 
@@ -90,6 +92,10 @@
     private static final String COMMAND_GET_MAX_PHONES = "get-max-phones";
     private static final String COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_FILTER =
             "set-test-emergency-phone-account-package-filter";
+    /**
+     * Command used to emit a distinct "mark" in the logs.
+     */
+    private static final String COMMAND_LOG_MARK = "log-mark";
 
     private ComponentName mComponent;
     private String mAccountId;
@@ -161,6 +167,8 @@
                         + " package name that will be used for test emergency calls. To clear,"
                         + " send an empty package name. Real emergency calls will still be placed"
                         + " over Telephony.\n"
+                + "telecom log-mark <MESSAGE>: emits a message into the telecom logs.  Useful for "
+                        + "testers to indicate where in the logs various test steps take place.\n"
         );
     }
 
@@ -265,6 +273,9 @@
             case COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_FILTER:
                 runSetEmergencyPhoneAccountPackageFilter();
                 break;
+            case COMMAND_LOG_MARK:
+                runLogMark();
+                break;
             default:
                 Log.w(this, "onRun: unknown command: %s", command);
                 throw new IllegalArgumentException ("unknown command '" + command + "'");
@@ -442,6 +453,11 @@
 
     }
 
+    private void runLogMark() throws RemoteException {
+        String message = Arrays.stream(mArgs.peekRemainingArgs()).collect(Collectors.joining(" "));
+        mTelecomService.requestLogMark(message);
+    }
+
     private PhoneAccountHandle getPhoneAccountHandleFromArgs() throws RemoteException {
         if (TextUtils.isEmpty(mArgs.peekNextArg())) {
             return null;
diff --git a/core/api/current.txt b/core/api/current.txt
index b08ffc9..e260ad0 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -335,15 +335,15 @@
     field public static final int allowClearUserData = 16842757; // 0x1010005
     field public static final int allowClickWhenDisabled = 16844312; // 0x1010618
     field public static final int allowEmbedded = 16843765; // 0x10103f5
-    field public static final int allowGameAngleDriver;
-    field public static final int allowGameDownscaling;
-    field public static final int allowGameFpsOverride;
+    field public static final int allowGameAngleDriver = 16844376; // 0x1010658
+    field public static final int allowGameDownscaling = 16844377; // 0x1010659
+    field public static final int allowGameFpsOverride = 16844378; // 0x101065a
     field public static final int allowNativeHeapPointerTagging = 16844306; // 0x1010612
     field public static final int allowParallelSyncs = 16843570; // 0x1010332
     field public static final int allowSingleTap = 16843353; // 0x1010259
     field public static final int allowTaskReparenting = 16843268; // 0x1010204
     field public static final int allowUndo = 16843999; // 0x10104df
-    field public static final int allowUntrustedActivityEmbedding;
+    field public static final int allowUntrustedActivityEmbedding = 16844393; // 0x1010669
     field public static final int alpha = 16843551; // 0x101031f
     field public static final int alphabeticModifiers = 16844110; // 0x101054e
     field public static final int alphabeticShortcut = 16843235; // 0x10101e3
@@ -374,7 +374,7 @@
     field public static final int authorities = 16842776; // 0x1010018
     field public static final int autoAdvanceViewId = 16843535; // 0x101030f
     field public static final int autoCompleteTextViewStyle = 16842859; // 0x101006b
-    field public static final int autoHandwritingEnabled;
+    field public static final int autoHandwritingEnabled = 16844382; // 0x101065e
     field public static final int autoLink = 16842928; // 0x10100b0
     field public static final int autoMirrored = 16843754; // 0x10103ea
     field public static final int autoRemoveFromRecents = 16843847; // 0x1010447
@@ -390,7 +390,7 @@
     field public static final int autoVerify = 16844014; // 0x10104ee
     field public static final int autofillHints = 16844118; // 0x1010556
     field public static final int autofilledHighlight = 16844136; // 0x1010568
-    field public static final int backdropColor;
+    field public static final int backdropColor = 16844402; // 0x1010672
     field public static final int background = 16842964; // 0x10100d4
     field public static final int backgroundDimAmount = 16842802; // 0x1010032
     field public static final int backgroundDimEnabled = 16843295; // 0x101021f
@@ -437,7 +437,7 @@
     field public static final int calendarViewShown = 16843596; // 0x101034c
     field public static final int calendarViewStyle = 16843613; // 0x101035d
     field public static final int canControlMagnification = 16844039; // 0x1010507
-    field public static final int canDisplayOnRemoteDevices;
+    field public static final int canDisplayOnRemoteDevices = 16844368; // 0x1010650
     field public static final int canPauseRecording = 16844314; // 0x101061a
     field public static final int canPerformGestures = 16844045; // 0x101050d
     field public static final int canRecord = 16844060; // 0x101051c
@@ -632,7 +632,7 @@
     field public static final int elevation = 16843840; // 0x1010440
     field public static final int ellipsize = 16842923; // 0x10100ab
     field public static final int ems = 16843096; // 0x1010158
-    field public static final int enableOnBackInvokedCallback;
+    field public static final int enableOnBackInvokedCallback = 16844396; // 0x101066c
     field public static final int enableVrMode = 16844069; // 0x1010525
     field public static final int enabled = 16842766; // 0x101000e
     field public static final int end = 16843996; // 0x10104dc
@@ -744,10 +744,10 @@
     field public static final int freezesText = 16843116; // 0x101016c
     field public static final int fromAlpha = 16843210; // 0x10101ca
     field public static final int fromDegrees = 16843187; // 0x10101b3
-    field public static final int fromExtendBottom;
-    field public static final int fromExtendLeft;
-    field public static final int fromExtendRight;
-    field public static final int fromExtendTop;
+    field public static final int fromExtendBottom = 16844386; // 0x1010662
+    field public static final int fromExtendLeft = 16844383; // 0x101065f
+    field public static final int fromExtendRight = 16844385; // 0x1010661
+    field public static final int fromExtendTop = 16844384; // 0x1010660
     field public static final int fromId = 16843850; // 0x101044a
     field public static final int fromScene = 16843741; // 0x10103dd
     field public static final int fromXDelta = 16843206; // 0x10101c6
@@ -867,7 +867,7 @@
     field public static final int installLocation = 16843447; // 0x10102b7
     field public static final int interactiveUiTimeout = 16844181; // 0x1010595
     field public static final int interpolator = 16843073; // 0x1010141
-    field public static final int intro;
+    field public static final int intro = 16844395; // 0x101066b
     field public static final int isAccessibilityTool = 16844353; // 0x1010641
     field public static final int isAlwaysSyncable = 16843571; // 0x1010333
     field public static final int isAsciiCapable = 16843753; // 0x10103e9
@@ -910,7 +910,7 @@
     field public static final int keyboardNavigationCluster = 16844096; // 0x1010540
     field public static final int keycode = 16842949; // 0x10100c5
     field public static final int killAfterRestore = 16843420; // 0x101029c
-    field public static final int knownActivityEmbeddingCerts;
+    field public static final int knownActivityEmbeddingCerts = 16844394; // 0x101066a
     field public static final int knownCerts = 16844330; // 0x101062a
     field public static final int lStar = 16844359; // 0x1010647
     field public static final int label = 16842753; // 0x1010001
@@ -978,8 +978,8 @@
     field public static final int left = 16843181; // 0x10101ad
     field public static final int letterSpacing = 16843958; // 0x10104b6
     field public static final int level = 16844032; // 0x1010500
-    field public static final int lineBreakStyle;
-    field public static final int lineBreakWordStyle;
+    field public static final int lineBreakStyle = 16844398; // 0x101066e
+    field public static final int lineBreakWordStyle = 16844399; // 0x101066f
     field public static final int lineHeight = 16844159; // 0x101057f
     field public static final int lineSpacingExtra = 16843287; // 0x1010217
     field public static final int lineSpacingMultiplier = 16843288; // 0x1010218
@@ -1003,7 +1003,7 @@
     field public static final int listSeparatorTextViewStyle = 16843272; // 0x1010208
     field public static final int listViewStyle = 16842868; // 0x1010074
     field public static final int listViewWhiteStyle = 16842869; // 0x1010075
-    field public static final int localeConfig;
+    field public static final int localeConfig = 16844379; // 0x101065b
     field public static final int lockTaskMode = 16844013; // 0x10104ed
     field public static final int logo = 16843454; // 0x10102be
     field public static final int logoDescription = 16844009; // 0x10104e9
@@ -1162,7 +1162,7 @@
     field public static final int popupWindowStyle = 16842870; // 0x1010076
     field public static final int port = 16842793; // 0x1010029
     field public static final int positiveButtonText = 16843253; // 0x10101f5
-    field public static final int preferKeepClear;
+    field public static final int preferKeepClear = 16844381; // 0x101065d
     field public static final int preferMinimalPostProcessing = 16844300; // 0x101060c
     field public static final int preferenceCategoryStyle = 16842892; // 0x101008c
     field public static final int preferenceFragmentStyle = 16844038; // 0x1010506
@@ -1238,10 +1238,10 @@
     field public static final int requiredFeature = 16844116; // 0x1010554
     field public static final int requiredForAllUsers = 16843728; // 0x10103d0
     field public static final int requiredNotFeature = 16844117; // 0x1010555
-    field public static final int requiredSplitTypes;
+    field public static final int requiredSplitTypes = 16844366; // 0x101064e
     field public static final int requiresFadingEdge = 16843685; // 0x10103a5
     field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364
-    field public static final int resetEnabledSettingsOnAppDataCleared;
+    field public static final int resetEnabledSettingsOnAppDataCleared = 16844370; // 0x1010652
     field public static final int resizeClip = 16843983; // 0x10104cf
     field public static final int resizeMode = 16843619; // 0x1010363
     field public static final int resizeable = 16843405; // 0x101028d
@@ -1337,7 +1337,7 @@
     field public static final int shareInterpolator = 16843195; // 0x10101bb
     field @Deprecated public static final int sharedUserId = 16842763; // 0x101000b
     field @Deprecated public static final int sharedUserLabel = 16843361; // 0x1010261
-    field public static final int sharedUserMaxSdkVersion;
+    field public static final int sharedUserMaxSdkVersion = 16844365; // 0x101064d
     field public static final int shell = 16844180; // 0x1010594
     field public static final int shortcutDisabledMessage = 16844075; // 0x101052b
     field public static final int shortcutId = 16844072; // 0x1010528
@@ -1346,8 +1346,8 @@
     field public static final int shouldDisableView = 16843246; // 0x10101ee
     field public static final int shouldUseDefaultUnfoldTransition = 16844364; // 0x101064c
     field public static final int showAsAction = 16843481; // 0x10102d9
-    field public static final int showBackdrop;
-    field public static final int showClockAndComplications;
+    field public static final int showBackdrop = 16844380; // 0x101065c
+    field public static final int showClockAndComplications = 16844372; // 0x1010654
     field public static final int showDefault = 16843258; // 0x10101fa
     field public static final int showDividers = 16843561; // 0x1010329
     field public static final int showForAllUsers = 16844015; // 0x10104ef
@@ -1378,7 +1378,7 @@
     field public static final int splitMotionEvents = 16843503; // 0x10102ef
     field public static final int splitName = 16844105; // 0x1010549
     field public static final int splitTrack = 16843852; // 0x101044c
-    field public static final int splitTypes;
+    field public static final int splitTypes = 16844367; // 0x101064f
     field public static final int spotShadowAlpha = 16843967; // 0x10104bf
     field public static final int src = 16843033; // 0x1010119
     field public static final int ssp = 16843747; // 0x10103e3
@@ -1449,18 +1449,18 @@
     field public static final int summaryColumn = 16843426; // 0x10102a2
     field public static final int summaryOff = 16843248; // 0x10101f0
     field public static final int summaryOn = 16843247; // 0x10101ef
-    field public static final int supportedTypes;
+    field public static final int supportedTypes = 16844369; // 0x1010651
     field public static final int supportsAssist = 16844016; // 0x10104f0
-    field public static final int supportsBatteryGameMode;
+    field public static final int supportsBatteryGameMode = 16844374; // 0x1010656
     field public static final int supportsInlineSuggestions = 16844301; // 0x101060d
-    field public static final int supportsInlineSuggestionsWithTouchExploration;
+    field public static final int supportsInlineSuggestionsWithTouchExploration = 16844397; // 0x101066d
     field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
     field public static final int supportsLocalInteraction = 16844047; // 0x101050f
     field public static final int supportsMultipleDisplays = 16844182; // 0x1010596
-    field public static final int supportsPerformanceGameMode;
+    field public static final int supportsPerformanceGameMode = 16844375; // 0x1010657
     field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
     field public static final int supportsRtl = 16843695; // 0x10103af
-    field public static final int supportsStylusHandwriting;
+    field public static final int supportsStylusHandwriting = 16844371; // 0x1010653
     field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb
     field public static final int supportsUploading = 16843419; // 0x101029b
     field public static final int suppressesSpellChecker = 16844355; // 0x1010643
@@ -1579,7 +1579,7 @@
     field public static final int tileMode = 16843265; // 0x1010201
     field public static final int tileModeX = 16843895; // 0x1010477
     field public static final int tileModeY = 16843896; // 0x1010478
-    field public static final int tileService;
+    field public static final int tileService = 16844391; // 0x1010667
     field public static final int timePickerDialogTheme = 16843934; // 0x101049e
     field public static final int timePickerMode = 16843956; // 0x10104b4
     field public static final int timePickerStyle = 16843933; // 0x101049d
@@ -1598,10 +1598,10 @@
     field public static final int titleTextStyle = 16843512; // 0x10102f8
     field public static final int toAlpha = 16843211; // 0x10101cb
     field public static final int toDegrees = 16843188; // 0x10101b4
-    field public static final int toExtendBottom;
-    field public static final int toExtendLeft;
-    field public static final int toExtendRight;
-    field public static final int toExtendTop;
+    field public static final int toExtendBottom = 16844390; // 0x1010666
+    field public static final int toExtendLeft = 16844387; // 0x1010663
+    field public static final int toExtendRight = 16844389; // 0x1010665
+    field public static final int toExtendTop = 16844388; // 0x1010664
     field public static final int toId = 16843849; // 0x1010449
     field public static final int toScene = 16843742; // 0x10103de
     field public static final int toXDelta = 16843207; // 0x10101c7
@@ -1753,7 +1753,7 @@
     field public static final int windowSplashScreenAnimatedIcon = 16844333; // 0x101062d
     field @Deprecated public static final int windowSplashScreenAnimationDuration = 16844334; // 0x101062e
     field public static final int windowSplashScreenBackground = 16844332; // 0x101062c
-    field public static final int windowSplashScreenBehavior;
+    field public static final int windowSplashScreenBehavior = 16844392; // 0x1010668
     field public static final int windowSplashScreenBrandingImage = 16844335; // 0x101062f
     field public static final int windowSplashScreenIconBackgroundColor = 16844336; // 0x1010630
     field @Deprecated public static final int windowSplashscreenContent = 16844132; // 0x1010564
@@ -2092,7 +2092,7 @@
     field public static final int accessibilityActionScrollUp = 16908344; // 0x1020038
     field public static final int accessibilityActionSetProgress = 16908349; // 0x102003d
     field public static final int accessibilityActionShowOnScreen = 16908342; // 0x1020036
-    field public static final int accessibilityActionShowTextSuggestions;
+    field public static final int accessibilityActionShowTextSuggestions = 16908376; // 0x1020058
     field public static final int accessibilityActionShowTooltip = 16908356; // 0x1020044
     field public static final int accessibilitySystemActionBack = 16908363; // 0x102004b
     field public static final int accessibilitySystemActionHome = 16908364; // 0x102004c
@@ -2128,8 +2128,8 @@
     field public static final int icon_frame = 16908350; // 0x102003e
     field public static final int input = 16908297; // 0x1020009
     field public static final int inputArea = 16908318; // 0x102001e
-    field public static final int inputExtractAccessories;
-    field public static final int inputExtractAction;
+    field public static final int inputExtractAccessories = 16908378; // 0x102005a
+    field public static final int inputExtractAction = 16908377; // 0x1020059
     field public static final int inputExtractEditText = 16908325; // 0x1020025
     field @Deprecated public static final int keyboardView = 16908326; // 0x1020026
     field public static final int list = 16908298; // 0x102000a
@@ -2301,7 +2301,7 @@
     field public static final int TextAppearance = 16973886; // 0x103003e
     field public static final int TextAppearance_DeviceDefault = 16974253; // 0x10301ad
     field public static final int TextAppearance_DeviceDefault_DialogWindowTitle = 16974264; // 0x10301b8
-    field public static final int TextAppearance_DeviceDefault_Headline;
+    field public static final int TextAppearance_DeviceDefault_Headline = 16974565; // 0x10302e5
     field public static final int TextAppearance_DeviceDefault_Inverse = 16974254; // 0x10301ae
     field public static final int TextAppearance_DeviceDefault_Large = 16974255; // 0x10301af
     field public static final int TextAppearance_DeviceDefault_Large_Inverse = 16974256; // 0x10301b0
@@ -30977,7 +30977,7 @@
     field public static final int R = 30; // 0x1e
     field public static final int S = 31; // 0x1f
     field public static final int S_V2 = 32; // 0x20
-    field public static final int TIRAMISU = 10000; // 0x2710
+    field public static final int TIRAMISU = 33; // 0x21
   }
 
   public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -52021,7 +52021,6 @@
     method public boolean isSelected();
     method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean);
     method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean, boolean);
-    method @Deprecated @NonNull public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(@Nullable String, int, int, @Nullable String, int, int, boolean, boolean);
   }
 
   public static final class AccessibilityNodeInfo.CollectionItemInfo.Builder {
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 608a9a4..1fa1e89 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -525,6 +525,14 @@
 
 }
 
+package android.view.accessibility {
+
+  public static final class AccessibilityNodeInfo.CollectionItemInfo {
+    method @Deprecated @NonNull public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(@Nullable String, int, int, @Nullable String, int, int, boolean, boolean);
+  }
+
+}
+
 package android.view.translation {
 
   public final class TranslationManager {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 650de2e0..b25d1e3 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -367,12 +367,12 @@
 
   public static final class R.array {
     field public static final int config_keySystemUuidMapping = 17235973; // 0x1070005
-    field public static final int config_optionalIpSecAlgorithms;
+    field public static final int config_optionalIpSecAlgorithms = 17235974; // 0x1070006
   }
 
   public static final class R.attr {
     field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
-    field public static final int gameSessionService;
+    field public static final int gameSessionService = 16844373; // 0x1010655
     field public static final int hotwordDetectionService = 16844326; // 0x1010626
     field public static final int isVrOnly = 16844152; // 0x1010578
     field public static final int minExtensionVersion = 16844305; // 0x1010611
@@ -385,7 +385,7 @@
   }
 
   public static final class R.bool {
-    field public static final int config_enableQrCodeScannerOnLockScreen;
+    field public static final int config_enableQrCodeScannerOnLockScreen = 17891336; // 0x1110008
     field public static final int config_sendPackageName = 17891328; // 0x1110000
     field public static final int config_showDefaultAssistant = 17891329; // 0x1110001
     field public static final int config_showDefaultEmergency = 17891330; // 0x1110002
@@ -402,7 +402,7 @@
 
   public static final class R.drawable {
     field public static final int ic_info = 17301684; // 0x10800b4
-    field public static final int ic_safety_protection;
+    field public static final int ic_safety_protection = 17301685; // 0x10800b5
   }
 
   public static final class R.raw {
@@ -414,13 +414,13 @@
     field public static final int config_customMediaKeyDispatcher = 17039404; // 0x104002c
     field public static final int config_customMediaSessionPolicyProvider = 17039405; // 0x104002d
     field public static final int config_defaultAssistant = 17039393; // 0x1040021
-    field public static final int config_defaultAutomotiveNavigation;
+    field public static final int config_defaultAutomotiveNavigation = 17039424; // 0x1040040
     field public static final int config_defaultBrowser = 17039394; // 0x1040022
     field public static final int config_defaultCallRedirection = 17039397; // 0x1040025
     field public static final int config_defaultCallScreening = 17039398; // 0x1040026
     field public static final int config_defaultDialer = 17039395; // 0x1040023
     field public static final int config_defaultSms = 17039396; // 0x1040024
-    field public static final int config_devicePolicyManagement;
+    field public static final int config_devicePolicyManagement = 17039421; // 0x104003d
     field public static final int config_feedbackIntentExtraKey = 17039391; // 0x104001f
     field public static final int config_feedbackIntentNameKey = 17039392; // 0x1040020
     field public static final int config_helpIntentExtraKey = 17039389; // 0x104001d
@@ -429,19 +429,19 @@
     field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
     field public static final int config_systemActivityRecognizer = 17039416; // 0x1040038
     field public static final int config_systemAmbientAudioIntelligence = 17039411; // 0x1040033
-    field public static final int config_systemAppProtectionService;
+    field public static final int config_systemAppProtectionService = 17039422; // 0x104003e
     field public static final int config_systemAudioIntelligence = 17039412; // 0x1040034
-    field public static final int config_systemAutomotiveCalendarSyncManager;
+    field public static final int config_systemAutomotiveCalendarSyncManager = 17039423; // 0x104003f
     field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
     field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029
     field public static final int config_systemCompanionDeviceProvider = 17039417; // 0x1040039
     field public static final int config_systemContacts = 17039403; // 0x104002b
     field public static final int config_systemGallery = 17039399; // 0x1040027
     field public static final int config_systemNotificationIntelligence = 17039413; // 0x1040035
-    field public static final int config_systemSettingsIntelligence;
+    field public static final int config_systemSettingsIntelligence = 17039426; // 0x1040042
     field public static final int config_systemShell = 17039402; // 0x104002a
     field public static final int config_systemSpeechRecognizer = 17039406; // 0x104002e
-    field public static final int config_systemSupervision;
+    field public static final int config_systemSupervision = 17039420; // 0x104003c
     field public static final int config_systemTelevisionNotificationHandler = 17039409; // 0x1040031
     field public static final int config_systemTextIntelligence = 17039414; // 0x1040036
     field public static final int config_systemUi = 17039418; // 0x104003a
@@ -449,7 +449,7 @@
     field public static final int config_systemVisualIntelligence = 17039415; // 0x1040037
     field public static final int config_systemWellbeing = 17039408; // 0x1040030
     field public static final int config_systemWifiCoexManager = 17039407; // 0x104002f
-    field public static final int safety_protection_display_text;
+    field public static final int safety_protection_display_text = 17039425; // 0x1040041
   }
 
   public static final class R.style {
@@ -762,7 +762,7 @@
     method public void clearRequireCompatChange();
     method public boolean isPendingIntentBackgroundActivityLaunchAllowed();
     method public static android.app.BroadcastOptions makeBasic();
-    method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void recordResponseEventWhileInBackground(@IntRange(from=0) long);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS) public void recordResponseEventWhileInBackground(@IntRange(from=0) long);
     method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean);
     method public void setDontSendToRestrictedApps(boolean);
     method public void setPendingIntentBackgroundActivityLaunchAllowed(boolean);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 27f5b92..f4a12a5 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -63,14 +63,14 @@
   public static final class R.bool {
     field public static final int config_assistantOnTopOfDream = 17891333; // 0x1110005
     field public static final int config_perDisplayFocusEnabled = 17891332; // 0x1110004
-    field public static final int config_preventImeStartupUnlessTextEditor;
+    field public static final int config_preventImeStartupUnlessTextEditor = 17891335; // 0x1110007
     field public static final int config_remoteInsetsControllerControlsSystemBars = 17891334; // 0x1110006
   }
 
   public static final class R.string {
     field public static final int config_defaultAssistant = 17039393; // 0x1040021
     field public static final int config_defaultDialer = 17039395; // 0x1040023
-    field public static final int config_systemAutomotiveCalendarSyncManager;
+    field public static final int config_systemAutomotiveCalendarSyncManager = 17039423; // 0x104003f
     field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
     field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029
     field public static final int config_systemGallery = 17039399; // 0x1040027
diff --git a/core/java/android/accessibilityservice/InputMethod.java b/core/java/android/accessibilityservice/InputMethod.java
index c0e5e84..a393604 100644
--- a/core/java/android/accessibilityservice/InputMethod.java
+++ b/core/java/android/accessibilityservice/InputMethod.java
@@ -624,7 +624,6 @@
         @Override
         public void invalidateInputInternal(EditorInfo editorInfo, IInputContext inputContext,
                 int sessionId) {
-            // TODO(b/217788708): Add automated test.
             if (mStartedInputConnection instanceof RemoteInputConnection) {
                 final RemoteInputConnection ric =
                         (RemoteInputConnection) mStartedInputConnection;
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index 8b3c9fa..56f8760 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -532,7 +532,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
+    @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS)
     public void recordResponseEventWhileInBackground(@IntRange(from = 0) long id) {
         mIdForResponseEvent = id;
     }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 4fbe232..df9f2a3 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -57,6 +57,7 @@
     @UnsupportedAppUsage
     void cancelNotificationWithTag(String pkg, String opPkg, String tag, int id, int userId);
 
+    boolean isInCall(String pkg, int uid);
     void setShowBadge(String pkg, int uid, boolean showBadge);
     boolean canShowBadge(String pkg, int uid);
     boolean hasSentValidMsg(String pkg, int uid);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 6e1d1cd..bfb1168 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -628,6 +628,9 @@
      * Bit to be bitwise-ored into the {@link #flags} field that should be
      * set if you would only like the sound, vibrate and ticker to be played
      * if the notification was not already showing.
+     *
+     * Note that using this flag will stop any ongoing alerting behaviour such
+     * as sound, vibration or blinking notification LED.
      */
     public static final int FLAG_ONLY_ALERT_ONCE    = 0x00000008;
 
@@ -4633,6 +4636,9 @@
          * Set this flag if you would only like the sound, vibrate
          * and ticker to be played if the notification is not already showing.
          *
+         * Note that using this flag will stop any ongoing alerting behaviour such
+         * as sound, vibration or blinking notification LED.
+         *
          * @see Notification#FLAG_ONLY_ALERT_ONCE
          */
         @NonNull
@@ -7917,6 +7923,8 @@
          * @hide
          */
         public MessagingStyle setShortcutIcon(@Nullable Icon conversationIcon) {
+            // TODO(b/228941516): This icon should be downscaled to avoid using too much memory,
+            // see reduceImageSizes.
             mShortcutIcon = conversationIcon;
             return this;
         }
@@ -8423,6 +8431,51 @@
             return makeMessagingView(StandardTemplateParams.VIEW_TYPE_HEADS_UP);
         }
 
+        /**
+         * @hide
+         */
+        @Override
+        public void reduceImageSizes(Context context) {
+            super.reduceImageSizes(context);
+            Resources resources = context.getResources();
+            boolean isLowRam = ActivityManager.isLowRamDeviceStatic();
+            if (mShortcutIcon != null) {
+                int maxSize = resources.getDimensionPixelSize(
+                        isLowRam ? R.dimen.notification_small_icon_size_low_ram
+                                : R.dimen.notification_small_icon_size);
+                mShortcutIcon.scaleDownIfNecessary(maxSize, maxSize);
+            }
+
+            int maxAvatarSize = resources.getDimensionPixelSize(
+                    isLowRam ? R.dimen.notification_person_icon_max_size
+                            : R.dimen.notification_person_icon_max_size_low_ram);
+            if (mUser != null && mUser.getIcon() != null) {
+                mUser.getIcon().scaleDownIfNecessary(maxAvatarSize, maxAvatarSize);
+            }
+
+            reduceMessagesIconSizes(mMessages, maxAvatarSize);
+            reduceMessagesIconSizes(mHistoricMessages, maxAvatarSize);
+        }
+
+        /**
+         * @hide
+         */
+        private static void reduceMessagesIconSizes(@Nullable List<Message> messages, int maxSize) {
+            if (messages == null) {
+                return;
+            }
+
+            for (Message message : messages) {
+                Person sender = message.mSender;
+                if (sender != null) {
+                    Icon icon = sender.getIcon();
+                    if (icon != null) {
+                        icon.scaleDownIfNecessary(maxSize, maxSize);
+                    }
+                }
+            }
+        }
+
         public static final class Message {
             /** @hide */
             public static final String KEY_TEXT = "text";
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index df7bf7b..add891d 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -313,6 +313,7 @@
      * one of the permitted values above.  The API is a string that is a legal Java simple
      * identifier.  The api is modified to conform to the system property style guide by
      * replacing every upper case letter with an underscore and the lower case equivalent.
+     * (An initial upper case letter is not prefixed with an underscore).
      * There is no requirement that the apiName be the name of an actual API.
      *
      * Be aware that SystemProperties has a maximum length which is private to the
@@ -326,7 +327,7 @@
             @NonNull String apiName) {
         char[] api = apiName.toCharArray();
         int upper = 0;
-        for (int i = 0; i < api.length; i++) {
+        for (int i = 1; i < api.length; i++) {
             if (Character.isUpperCase(api[i])) {
                 upper++;
             }
@@ -336,7 +337,9 @@
         for (int i = 0; i < api.length; i++) {
             if (Character.isJavaIdentifierPart(api[i])) {
                 if (Character.isUpperCase(api[i])) {
-                    suffix[j++] = '_';
+                    if (i > 0) {
+                        suffix[j++] = '_';
+                    }
                     suffix[j++] = Character.toLowerCase(api[i]);
                 } else {
                     suffix[j++] = api[i];
@@ -1286,13 +1289,23 @@
     }
 
     /**
-     * Return the name of the cache, to be used in debug messages.
+     * Return the name of the cache, to be used in debug messages.  This is exposed
+     * primarily for testing.
+     * @hide
      */
-    private final @NonNull String cacheName() {
+    public final @NonNull String cacheName() {
         return mCacheName;
     }
 
     /**
+     * Return the property used by the cache.  This is primarily for test purposes.
+     * @hide
+     */
+    public final @NonNull String propertyName() {
+        return mPropertyName;
+    }
+
+    /**
      * Return the query as a string, to be used in debug messages.  New clients should not
      * override this, but should instead add the necessary toString() method to the Query
      * class.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index d375a9e..d11b23c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -185,6 +185,30 @@
         mResourcesManager = new DevicePolicyResourcesManager(context, service);
     }
 
+    /**
+     * Fetch the current value of mService.  This is used in the binder cache lambda
+     * expressions.
+     */
+    private final IDevicePolicyManager getService() {
+        return mService;
+    }
+
+    /**
+     * Fetch the current value of mParentInstance.  This is used in the binder cache
+     * lambda expressions.
+     */
+    private final boolean isParentInstance() {
+        return mParentInstance;
+    }
+
+    /**
+     * Fetch the current value of mContext.  This is used in the binder cache lambda
+     * expressions.
+     */
+    private final Context getContext() {
+        return mContext;
+    }
+
     /** @hide test will override it. */
     @VisibleForTesting
     protected int myUserId() {
@@ -2518,7 +2542,7 @@
      * that has this delegation. If another app already had delegated security logging access, it
      * will lose the delegation when a new app is delegated.
      *
-     * <p> Can only be granted by Device Owner or Profile Owner of an organnization owned and
+     * <p> Can only be granted by Device Owner or Profile Owner of an organization-owned
      * managed profile.
      */
     public static final String DELEGATION_SECURITY_LOGGING = "delegation-security-logging";
@@ -3783,57 +3807,21 @@
             "android.app.extra.RESOURCE_IDS";
 
     /**
-     * A convenience class that wraps some IpcDataCache methods. Instantiate it with an
-     * API string. Instances can and should be final static. All instances of this class
-     * use the same key for invalidation.
+     * This object is a single place to tack on invalidation and disable calls.  All
+     * binder caches in this class derive from this Config, so all can be invalidated or
+     * disabled through this Config.
      */
-    private static class BinderApi {
-        private final static String KEY = "DevicePolicyManager";
-        private final String mApi;
-        BinderApi(String api) {
-            mApi = api;
-        }
-        final String api() {
-            return mApi;
-        }
-        final String key() {
-            return KEY;
-        }
-        final static void invalidate() {
-            IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM, KEY);
-        }
-        final void disable() {
-            IpcDataCache.disableForCurrentProcess(mApi);
-        }
-    }
+    private static final IpcDataCache.Config sDpmCaches =
+            new IpcDataCache.Config(8, IpcDataCache.MODULE_SYSTEM, "DevicePolicyManagerCaches");
 
     /** @hide */
     public static void invalidateBinderCaches() {
-        BinderApi.invalidate();
+        sDpmCaches.invalidateCache();
     }
 
-    /**
-     * A simple wrapper for binder caches in this class. All caches are created with a
-     * maximum of 8 entries, the SYSTEM module, and a cache name that is the same as the api.
-     */
-    private static class BinderCache<Q,R> extends IpcDataCache<Q,R> {
-        BinderCache(BinderApi api, IpcDataCache.QueryHandler<Q,R> handler) {
-            super(8, IpcDataCache.MODULE_SYSTEM, api.key(), api.api(), handler);
-        }
-    }
-
-    /**
-     * Disable all caches in the local process.
-     * @hide
-     */
-    public static void disableLocalProcessCaches() {
-        disableGetKeyguardDisabledFeaturesCache();
-        disableHasDeviceOwnerCache();
-        disableGetProfileOwnerOrDeviceOwnerSupervisionComponentCache();
-        disableIsOrganizationOwnedDeviceWithManagedProfileCache();
-        disableGetDeviceOwnerOrganizationNameCache();
-        disableGetOrganizationNameForUserCache();
-        disableIsNetworkLoggingEnabled();
+    /** @hide */
+    public static void disableLocalCaches() {
+        sDpmCaches.disableAllForCurrentProcess();
     }
 
     /** @hide */
@@ -8435,54 +8423,16 @@
         return getKeyguardDisabledFeatures(admin, myUserId());
     }
 
-    // A key into the keyguard cache.
-    private static class KeyguardQuery {
-        private final ComponentName mAdmin;
-        private final int mUserHandle;
-        KeyguardQuery(@Nullable ComponentName admin, int userHandle) {
-            mAdmin = admin;
-            mUserHandle = userHandle;
-        }
-        public boolean equals(Object o) {
-            if (o instanceof KeyguardQuery) {
-                KeyguardQuery r = (KeyguardQuery) o;
-                return Objects.equals(mAdmin, r.mAdmin) && mUserHandle == r.mUserHandle;
-            } else {
-                return false;
-            }
-        }
-        public int hashCode() {
-            return ((mAdmin != null) ? mAdmin.hashCode() : 0) * 13 + mUserHandle;
-        }
-    }
-
-    // The query handler does not cache wildcard user IDs, although they should never
-    // appear in the query.
-    private static final BinderApi sGetKeyguardDisabledFeatures =
-            new BinderApi("getKeyguardDisabledFeatures");
-    private BinderCache<KeyguardQuery, Integer> mGetKeyGuardDisabledFeaturesCache =
-            new BinderCache<>(sGetKeyguardDisabledFeatures,
-                    new IpcDataCache.QueryHandler<KeyguardQuery, Integer>() {
-                        @Override
-                        public Integer apply(KeyguardQuery query) {
-                            try {
-                                return mService.getKeyguardDisabledFeatures(
-                                    query.mAdmin, query.mUserHandle, mParentInstance);
-                            } catch (RemoteException e) {
-                                throw e.rethrowFromSystemServer();
-                            }
-                        }});
-
-    /** @hide */
-    public static void disableGetKeyguardDisabledFeaturesCache() {
-        sGetKeyguardDisabledFeatures.disable();
-    }
+    private IpcDataCache<Pair<ComponentName, Integer>, Integer> mGetKeyGuardDisabledFeaturesCache =
+            new IpcDataCache<>(sDpmCaches.child("getKeyguardDisabledFeatures"),
+                    (query) -> getService().getKeyguardDisabledFeatures(
+                            query.first, query.second, isParentInstance()));
 
     /** @hide per-user version */
     @UnsupportedAppUsage
     public int getKeyguardDisabledFeatures(@Nullable ComponentName admin, int userHandle) {
         if (mService != null) {
-            return mGetKeyGuardDisabledFeaturesCache.query(new KeyguardQuery(admin, userHandle));
+            return mGetKeyGuardDisabledFeaturesCache.query(new Pair<>(admin, userHandle));
         } else {
             return KEYGUARD_DISABLE_FEATURES_NONE;
         }
@@ -8864,23 +8814,9 @@
         return name != null ? name.getPackageName() : null;
     }
 
-    private static final BinderApi sHasDeviceOwner =
-            new BinderApi("hasDeviceOwner");
-    private BinderCache<Void, Boolean> mHasDeviceOwnerCache =
-            new BinderCache<>(sHasDeviceOwner,
-                    new IpcDataCache.QueryHandler<Void, Boolean>() {
-                        @Override
-                        public Boolean apply(Void query) {
-                            try {
-                                return mService.hasDeviceOwner();
-                            } catch (RemoteException e) {
-                                throw e.rethrowFromSystemServer();
-                            }
-                        }});
-    /** @hide */
-    public static void disableHasDeviceOwnerCache() {
-        sHasDeviceOwner.disable();
-    }
+    private IpcDataCache<Void, Boolean> mHasDeviceOwnerCache =
+            new IpcDataCache<>(sDpmCaches.child("hasDeviceOwner"),
+                    (query) -> getService().hasDeviceOwner());
 
     /**
      * Called by the system to find out whether the device is managed by a Device Owner.
@@ -9256,25 +9192,10 @@
         return null;
     }
 
-    private final static BinderApi sGetProfileOwnerOrDeviceOwnerSupervisionComponent =
-            new BinderApi("getProfileOwnerOrDeviceOwnerSupervisionComponent");
-    private final BinderCache<UserHandle, ComponentName>
+    private final IpcDataCache<UserHandle, ComponentName>
             mGetProfileOwnerOrDeviceOwnerSupervisionComponentCache =
-            new BinderCache(sGetProfileOwnerOrDeviceOwnerSupervisionComponent,
-                    new IpcDataCache.QueryHandler<UserHandle, ComponentName>() {
-                        @Override
-                        public ComponentName apply(UserHandle user) {
-                            try {
-                                return mService.getProfileOwnerOrDeviceOwnerSupervisionComponent(
-                                    user);
-                            } catch (RemoteException re) {
-                                throw re.rethrowFromSystemServer();
-                            }
-                        }});
-    /** @hide */
-    public static void disableGetProfileOwnerOrDeviceOwnerSupervisionComponentCache() {
-        sGetProfileOwnerOrDeviceOwnerSupervisionComponent.disable();
-    }
+            new IpcDataCache<>(sDpmCaches.child("getProfileOwnerOrDeviceOwnerSupervisionComponent"),
+                    (arg) -> getService().getProfileOwnerOrDeviceOwnerSupervisionComponent(arg));
 
     /**
      * Returns the configured supervision app if it exists and is the device owner or policy owner.
@@ -9329,23 +9250,9 @@
         return null;
     }
 
-    private final static BinderApi sIsOrganizationOwnedDeviceWithManagedProfile =
-            new BinderApi("isOrganizationOwnedDeviceWithManagedProfile");
-    private final BinderCache<Void, Boolean> mIsOrganizationOwnedDeviceWithManagedProfileCache =
-            new BinderCache(sIsOrganizationOwnedDeviceWithManagedProfile,
-                    new IpcDataCache.QueryHandler<Void, Boolean>() {
-                        @Override
-                        public Boolean apply(Void query) {
-                            try {
-                                return mService.isOrganizationOwnedDeviceWithManagedProfile();
-                            } catch (RemoteException re) {
-                                throw re.rethrowFromSystemServer();
-                            }
-                        }});
-    /** @hide */
-    public static void disableIsOrganizationOwnedDeviceWithManagedProfileCache() {
-        sIsOrganizationOwnedDeviceWithManagedProfile.disable();
-    }
+    private final IpcDataCache<Void, Boolean> mIsOrganizationOwnedDeviceWithManagedProfileCache =
+            new IpcDataCache(sDpmCaches.child("isOrganizationOwnedDeviceWithManagedProfile"),
+                    (query) -> getService().isOrganizationOwnedDeviceWithManagedProfile());
 
     /**
      * Apps can use this method to find out if the device was provisioned as
@@ -12927,23 +12834,9 @@
         }
     }
 
-    private final static BinderApi sGetDeviceOwnerOrganizationName =
-            new BinderApi("getDeviceOwnerOrganizationName");
-    private final BinderCache<Void, CharSequence> mGetDeviceOwnerOrganizationNameCache =
-            new BinderCache(sGetDeviceOwnerOrganizationName,
-                    new IpcDataCache.QueryHandler<Void, CharSequence>() {
-                        @Override
-                        public CharSequence apply(Void query) {
-                            try {
-                                return mService.getDeviceOwnerOrganizationName();
-                            } catch (RemoteException re) {
-                                throw re.rethrowFromSystemServer();
-                            }
-                        }});
-    /** @hide */
-    public static void disableGetDeviceOwnerOrganizationNameCache() {
-        sGetDeviceOwnerOrganizationName.disable();
-    }
+    private final IpcDataCache<Void, CharSequence> mGetDeviceOwnerOrganizationNameCache =
+            new IpcDataCache(sDpmCaches.child("getDeviceOwnerOrganizationName"),
+                    (query) -> getService().getDeviceOwnerOrganizationName());
 
     /**
      * Called by the system to retrieve the name of the organization managing the device.
@@ -12960,23 +12853,9 @@
         return mGetDeviceOwnerOrganizationNameCache.query(null);
     }
 
-    private final static BinderApi sGetOrganizationNameForUser =
-            new BinderApi("getOrganizationNameForUser");
-    private final BinderCache<Integer, CharSequence> mGetOrganizationNameForUserCache =
-            new BinderCache(sGetOrganizationNameForUser,
-                    new IpcDataCache.QueryHandler<Integer, CharSequence>() {
-                        @Override
-                        public CharSequence apply(Integer userHandle) {
-                            try {
-                                return mService.getOrganizationNameForUser(userHandle);
-                            } catch (RemoteException re) {
-                                throw re.rethrowFromSystemServer();
-                            }
-                        }});
-    /** @hide */
-    public static void disableGetOrganizationNameForUserCache() {
-        sGetOrganizationNameForUser.disable();
-    }
+    private final IpcDataCache<Integer, CharSequence> mGetOrganizationNameForUserCache =
+            new IpcDataCache<>(sDpmCaches.child("getOrganizationNameForUser"),
+                    (query) -> getService().getOrganizationNameForUser(query));
 
     /**
      * Retrieve the default title message used in the confirm credentials screen for a given user.
@@ -13372,24 +13251,10 @@
         }
     }
 
-    private final static BinderApi sNetworkLoggingApi = new BinderApi("isNetworkLoggingEnabled");
-    private BinderCache<ComponentName, Boolean> mIsNetworkLoggingEnabledCache =
-            new BinderCache<>(sNetworkLoggingApi,
-                    new IpcDataCache.QueryHandler<ComponentName, Boolean>() {
-                        @Override
-                        public Boolean apply(ComponentName admin) {
-                            try {
-                                return mService.isNetworkLoggingEnabled(
-                                    admin, mContext.getPackageName());
-                            } catch (RemoteException re) {
-                                throw re.rethrowFromSystemServer();
-                            }
-                        }});
-
-    /** @hide */
-    public static void disableIsNetworkLoggingEnabled() {
-        sNetworkLoggingApi.disable();
-    }
+    private IpcDataCache<ComponentName, Boolean> mIsNetworkLoggingEnabledCache =
+            new IpcDataCache<>(sDpmCaches.child("isNetworkLoggingEnabled"),
+                    (admin) -> getService().isNetworkLoggingEnabled(admin,
+                            getContext().getPackageName()));
 
     /**
      * Return whether network logging is enabled by a device owner or profile owner of
diff --git a/core/java/android/app/admin/DevicePolicyResourcesManager.java b/core/java/android/app/admin/DevicePolicyResourcesManager.java
index 0672922..e8eb792 100644
--- a/core/java/android/app/admin/DevicePolicyResourcesManager.java
+++ b/core/java/android/app/admin/DevicePolicyResourcesManager.java
@@ -70,6 +70,7 @@
      *
      * <p>Important notes to consider when using this API:
      * <ul>
+     * <li> Updated resources are persisted over reboots.
      * <li>{@link #getDrawable} references the resource
      * {@link DevicePolicyDrawableResource#getResourceIdInCallingPackage()} in the
      * calling package each time it gets called. You have to ensure that the resource is always
@@ -381,7 +382,9 @@
      *
      * <p>Important notes to consider when using this API:
      * <ul>
-     * <li> {@link #getString} references the resource {@code callingPackageResourceId} in the
+     * <li> Updated resources are persisted over reboots.
+     * <li> {@link #getString} references the resource
+     * {@link DevicePolicyStringResource#getResourceIdInCallingPackage()} in the
      * calling package each time it gets called. You have to ensure that the resource is always
      * available in the calling package as long as it is used as an updated resource.
      * <li> You still have to re-call {@code setStrings} even if you only make changes to the
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index dea0834..cc62d533 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -180,7 +180,7 @@
       "name": "CtsAppSecurityHostTestCases",
       "options": [
         {
-          "include-filter": "com.android.cts.splitapp.SplitAppTest"
+          "include-filter": "android.appsecurity.cts.SplitTests"
         },
         {
           "include-filter": "android.appsecurity.cts.EphemeralTest"
@@ -199,14 +199,6 @@
       "name": "CtsRollbackManagerHostTestCases"
     },
     {
-      "name": "CtsOsHostTestCases",
-      "options": [
-        {
-          "include-filter": "com.android.server.pm.PackageParserTest"
-        }
-      ]
-    },
-    {
       "name": "CtsContentTestCases",
       "options": [
         {
diff --git a/core/java/android/hardware/CameraSessionStats.java b/core/java/android/hardware/CameraSessionStats.java
index 698cc76..9ef6306 100644
--- a/core/java/android/hardware/CameraSessionStats.java
+++ b/core/java/android/hardware/CameraSessionStats.java
@@ -61,6 +61,7 @@
     private boolean mDeviceError;
     private float mMaxPreviewFps;
     private ArrayList<CameraStreamStats> mStreamStats;
+    private String mUserTag;
 
     public CameraSessionStats() {
         mFacing = -1;
@@ -131,6 +132,7 @@
         dest.writeLong(mResultErrorCount);
         dest.writeBoolean(mDeviceError);
         dest.writeTypedList(mStreamStats);
+        dest.writeString(mUserTag);
     }
 
     public void readFromParcel(Parcel in) {
@@ -151,6 +153,8 @@
         ArrayList<CameraStreamStats> streamStats = new ArrayList<CameraStreamStats>();
         in.readTypedList(streamStats, CameraStreamStats.CREATOR);
         mStreamStats = streamStats;
+
+        mUserTag = in.readString();
     }
 
     public String getCameraId() {
@@ -208,4 +212,8 @@
     public List<CameraStreamStats> getStreamStats() {
         return mStreamStats;
     }
+
+    public String getUserTag() {
+        return mUserTag;
+    }
 }
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index d94ad3a..15e59e0 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -266,6 +266,8 @@
 
     private int mRequestType = -1;
 
+    private static final String SET_TAG_STRING_PREFIX =
+            "android.hardware.camera2.CaptureRequest.setTag.";
     /**
      * Get the type of the capture request
      *
@@ -614,6 +616,11 @@
                 throw new RuntimeException("Reading cached CaptureRequest is not supported");
             }
         }
+
+        boolean hasUserTagStr = (in.readInt() == 1) ? true : false;
+        if (hasUserTagStr) {
+            mUserTag = in.readString();
+        }
     }
 
     @Override
@@ -656,6 +663,19 @@
                 dest.writeInt(0);
             }
         }
+
+        // Write string for user tag if set to something in the same namespace
+        if (mUserTag != null) {
+            String userTagStr = mUserTag.toString();
+            if (userTagStr != null && userTagStr.startsWith(SET_TAG_STRING_PREFIX)) {
+                dest.writeInt(1);
+                dest.writeString(userTagStr.substring(SET_TAG_STRING_PREFIX.length()));
+            } else {
+                dest.writeInt(0);
+            }
+        } else {
+            dest.writeInt(0);
+        }
     }
 
     /**
@@ -938,7 +958,10 @@
          * <p>This tag is not used for anything by the camera device, but can be
          * used by an application to easily identify a CaptureRequest when it is
          * returned by
-         * {@link CameraCaptureSession.CaptureCallback#onCaptureCompleted CaptureCallback.onCaptureCompleted}
+         * {@link CameraCaptureSession.CaptureCallback#onCaptureCompleted CaptureCallback.onCaptureCompleted}.</p>
+         *
+         * <p>If the application overrides the tag object's {@link Object#toString} function, the
+         * returned string must not contain personal identifiable information.</p>
          *
          * @param tag an arbitrary Object to store with this request
          * @see CaptureRequest#getTag
@@ -1387,6 +1410,14 @@
      * region and output only the intersection rectangle as the metering region in the result
      * metadata.  If the region is entirely outside the crop region, it will be ignored and
      * not reported in the result metadata.</p>
+     * <p>When setting the AE metering regions, the application must consider the additional
+     * crop resulted from the aspect ratio differences between the preview stream and
+     * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}. For example, if the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is the full
+     * active array size with 4:3 aspect ratio, and the preview stream is 16:9,
+     * the boundary of AE regions will be [0, y_crop] and
+     * [active_width, active_height - 2 * y_crop] rather than [0, 0] and
+     * [active_width, active_height], where y_crop is the additional crop due to aspect ratio
+     * mismatch.</p>
      * <p>Starting from API level 30, the coordinate system of activeArraySize or
      * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not
      * pre-zoom field of view. This means that the same aeRegions values at different
@@ -1609,6 +1640,14 @@
      * region and output only the intersection rectangle as the metering region in the result
      * metadata. If the region is entirely outside the crop region, it will be ignored and
      * not reported in the result metadata.</p>
+     * <p>When setting the AF metering regions, the application must consider the additional
+     * crop resulted from the aspect ratio differences between the preview stream and
+     * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}. For example, if the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is the full
+     * active array size with 4:3 aspect ratio, and the preview stream is 16:9,
+     * the boundary of AF regions will be [0, y_crop] and
+     * [active_width, active_height - 2 * y_crop] rather than [0, 0] and
+     * [active_width, active_height], where y_crop is the additional crop due to aspect ratio
+     * mismatch.</p>
      * <p>Starting from API level 30, the coordinate system of activeArraySize or
      * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not
      * pre-zoom field of view. This means that the same afRegions values at different
@@ -1823,6 +1862,14 @@
      * region and output only the intersection rectangle as the metering region in the result
      * metadata.  If the region is entirely outside the crop region, it will be ignored and
      * not reported in the result metadata.</p>
+     * <p>When setting the AWB metering regions, the application must consider the additional
+     * crop resulted from the aspect ratio differences between the preview stream and
+     * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}. For example, if the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is the full
+     * active array size with 4:3 aspect ratio, and the preview stream is 16:9,
+     * the boundary of AWB regions will be [0, y_crop] and
+     * [active_width, active_height - 2 * y_crop] rather than [0, 0] and
+     * [active_width, active_height], where y_crop is the additional crop due to aspect ratio
+     * mismatch.</p>
      * <p>Starting from API level 30, the coordinate system of activeArraySize or
      * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not
      * pre-zoom field of view. This means that the same awbRegions values at different
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 60d5e9e..1faec5b 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -829,6 +829,14 @@
      * region and output only the intersection rectangle as the metering region in the result
      * metadata.  If the region is entirely outside the crop region, it will be ignored and
      * not reported in the result metadata.</p>
+     * <p>When setting the AE metering regions, the application must consider the additional
+     * crop resulted from the aspect ratio differences between the preview stream and
+     * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}. For example, if the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is the full
+     * active array size with 4:3 aspect ratio, and the preview stream is 16:9,
+     * the boundary of AE regions will be [0, y_crop] and
+     * [active_width, active_height - 2 * y_crop] rather than [0, 0] and
+     * [active_width, active_height], where y_crop is the additional crop due to aspect ratio
+     * mismatch.</p>
      * <p>Starting from API level 30, the coordinate system of activeArraySize or
      * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not
      * pre-zoom field of view. This means that the same aeRegions values at different
@@ -1301,6 +1309,14 @@
      * region and output only the intersection rectangle as the metering region in the result
      * metadata. If the region is entirely outside the crop region, it will be ignored and
      * not reported in the result metadata.</p>
+     * <p>When setting the AF metering regions, the application must consider the additional
+     * crop resulted from the aspect ratio differences between the preview stream and
+     * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}. For example, if the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is the full
+     * active array size with 4:3 aspect ratio, and the preview stream is 16:9,
+     * the boundary of AF regions will be [0, y_crop] and
+     * [active_width, active_height - 2 * y_crop] rather than [0, 0] and
+     * [active_width, active_height], where y_crop is the additional crop due to aspect ratio
+     * mismatch.</p>
      * <p>Starting from API level 30, the coordinate system of activeArraySize or
      * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not
      * pre-zoom field of view. This means that the same afRegions values at different
@@ -1926,6 +1942,14 @@
      * region and output only the intersection rectangle as the metering region in the result
      * metadata.  If the region is entirely outside the crop region, it will be ignored and
      * not reported in the result metadata.</p>
+     * <p>When setting the AWB metering regions, the application must consider the additional
+     * crop resulted from the aspect ratio differences between the preview stream and
+     * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}. For example, if the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is the full
+     * active array size with 4:3 aspect ratio, and the preview stream is 16:9,
+     * the boundary of AWB regions will be [0, y_crop] and
+     * [active_width, active_height - 2 * y_crop] rather than [0, 0] and
+     * [active_width, active_height], where y_crop is the additional crop due to aspect ratio
+     * mismatch.</p>
      * <p>Starting from API level 30, the coordinate system of activeArraySize or
      * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not
      * pre-zoom field of view. This means that the same awbRegions values at different
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 4fdd534..6ece5ef 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -101,6 +101,7 @@
 import android.view.Choreographer;
 import android.view.Gravity;
 import android.view.InputChannel;
+import android.view.InputDevice;
 import android.view.InputEventReceiver;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
@@ -134,7 +135,10 @@
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
 import android.widget.TextView;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
 import android.window.WindowMetricsHelper;
+import android.window.WindowOnBackInvokedDispatcher;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.inputmethod.IInputContentUriToken;
@@ -345,6 +349,9 @@
      **/
     private RingBuffer<MotionEvent> mPendingEvents;
 
+    /** Callback to handle back invocation when IME window is shown. */
+    private OnBackInvokedCallback mBackCallback;
+
     /**
      * Returns whether {@link InputMethodService} is responsible for rendering the back button and
      * the IME switcher button or not when the gestural navigation is enabled.
@@ -1605,6 +1612,7 @@
     @Override public void onDestroy() {
         mDestroyed = true;
         super.onDestroy();
+        unregisterOnBackInvokedCallback();
         mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
                 mInsetsComputer);
         doFinishInput();
@@ -2579,6 +2587,7 @@
         cancelImeSurfaceRemoval();
         mInShowWindow = false;
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+        registerOnBackInvokedCallback();
     }
 
 
@@ -2625,6 +2634,56 @@
     }
 
     /**
+     * Registers an {@link OnBackInvokedCallback} to handle back invocation when ahead-of-time
+     *  back dispatching is enabled. We keep the KEYCODE_BACK based legacy code around to handle
+     *  back on older devices.
+     */
+    private void registerOnBackInvokedCallback() {
+        if (mBackCallback != null) {
+            // A back callback has already been registered.
+            return;
+        }
+        final ViewRootImpl viewRootImpl = mRootView == null ? null : mRootView.getViewRootImpl();
+        if (viewRootImpl != null && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(
+                viewRootImpl.mContext)) {
+            final OnBackInvokedCallback callback = () -> {
+                KeyEvent downEvent = createKeyEvent(
+                        KeyEvent.ACTION_DOWN, false /* isTracking */);
+                onKeyDown(KeyEvent.KEYCODE_BACK, downEvent);
+                boolean hasStartedTracking =
+                        (downEvent.getFlags() & KeyEvent.FLAG_START_TRACKING) != 0;
+                KeyEvent upEvent = createKeyEvent(KeyEvent.ACTION_UP, hasStartedTracking);
+                onKeyUp(KeyEvent.KEYCODE_BACK, upEvent);
+            };
+            viewRootImpl.getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+                    OnBackInvokedDispatcher.PRIORITY_DEFAULT, callback);
+            mBackCallback = callback;
+        }
+    }
+
+    private KeyEvent createKeyEvent(int action, boolean isTracking) {
+        final long when = SystemClock.uptimeMillis();
+        return new KeyEvent(when, when, action,
+                KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */,
+                KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
+                KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY
+                        | (isTracking ? KeyEvent.FLAG_TRACKING : 0),
+                InputDevice.SOURCE_KEYBOARD);
+    }
+
+    private void unregisterOnBackInvokedCallback() {
+        final ViewRootImpl viewRootImpl = mRootView == null ? null : mRootView.getViewRootImpl();
+        if (viewRootImpl != null
+                && mBackCallback != null
+                && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(
+                viewRootImpl.mContext)) {
+            viewRootImpl.getOnBackInvokedDispatcher()
+                    .unregisterOnBackInvokedCallback(mBackCallback);
+        }
+        mBackCallback = null;
+    }
+
+    /**
      * Applies the IME visibility in {@link android.view.ImeInsetsSourceConsumer}.
      *
      * @param setVisible {@code true} to make it visible, false to hide it.
@@ -2669,6 +2728,7 @@
         }
         mLastWasInFullscreenMode = mIsFullscreen;
         updateFullscreenMode();
+        unregisterOnBackInvokedCallback();
     }
 
     /**
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
index 69e6313..2d1a3fe 100644
--- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
@@ -57,9 +57,11 @@
     @NonNull private final Set<Integer> mAllowedSpecificCarrierIds;
 
     private static final String ROAMING_MATCH_KEY = "mRoamingMatchCriteria";
+    private static final int DEFAULT_ROAMING_MATCH_CRITERIA = MATCH_ANY;
     private final int mRoamingMatchCriteria;
 
     private static final String OPPORTUNISTIC_MATCH_KEY = "mOpportunisticMatchCriteria";
+    private static final int DEFAULT_OPPORTUNISTIC_MATCH_CRITERIA = MATCH_ANY;
     private final int mOpportunisticMatchCriteria;
 
     private VcnCellUnderlyingNetworkTemplate(
@@ -253,23 +255,31 @@
     /** @hide */
     @Override
     void dumpTransportSpecificFields(IndentingPrintWriter pw) {
-        pw.println("mAllowedNetworkPlmnIds: " + mAllowedNetworkPlmnIds.toString());
-        pw.println("mAllowedSpecificCarrierIds: " + mAllowedSpecificCarrierIds.toString());
-        pw.println("mRoamingMatchCriteria: " + getMatchCriteriaString(mRoamingMatchCriteria));
-        pw.println(
-                "mOpportunisticMatchCriteria: "
-                        + getMatchCriteriaString(mOpportunisticMatchCriteria));
+        if (!mAllowedNetworkPlmnIds.isEmpty()) {
+            pw.println("mAllowedNetworkPlmnIds: " + mAllowedNetworkPlmnIds);
+        }
+        if (!mAllowedNetworkPlmnIds.isEmpty()) {
+            pw.println("mAllowedSpecificCarrierIds: " + mAllowedSpecificCarrierIds);
+        }
+        if (mRoamingMatchCriteria != DEFAULT_ROAMING_MATCH_CRITERIA) {
+            pw.println("mRoamingMatchCriteria: " + getMatchCriteriaString(mRoamingMatchCriteria));
+        }
+        if (mOpportunisticMatchCriteria != DEFAULT_OPPORTUNISTIC_MATCH_CRITERIA) {
+            pw.println(
+                    "mOpportunisticMatchCriteria: "
+                            + getMatchCriteriaString(mOpportunisticMatchCriteria));
+        }
     }
 
     /** This class is used to incrementally build VcnCellUnderlyingNetworkTemplate objects. */
     public static final class Builder {
-        private int mMeteredMatchCriteria = MATCH_ANY;
+        private int mMeteredMatchCriteria = DEFAULT_METERED_MATCH_CRITERIA;
 
         @NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>();
         @NonNull private final Set<Integer> mAllowedSpecificCarrierIds = new ArraySet<>();
 
-        private int mRoamingMatchCriteria = MATCH_ANY;
-        private int mOpportunisticMatchCriteria = MATCH_ANY;
+        private int mRoamingMatchCriteria = DEFAULT_ROAMING_MATCH_CRITERIA;
+        private int mOpportunisticMatchCriteria = DEFAULT_OPPORTUNISTIC_MATCH_CRITERIA;
 
         private int mMinEntryUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
         private int mMinExitUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
index 3a9ca3e..9235d09 100644
--- a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
@@ -15,8 +15,6 @@
  */
 package android.net.vcn;
 
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
-
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
 
 import android.annotation.IntDef;
@@ -88,6 +86,9 @@
     /** @hide */
     static final String METERED_MATCH_KEY = "mMeteredMatchCriteria";
 
+    /** @hide */
+    static final int DEFAULT_METERED_MATCH_CRITERIA = MATCH_ANY;
+
     private final int mMeteredMatchCriteria;
 
     /** @hide */
@@ -237,11 +238,21 @@
         pw.println(this.getClass().getSimpleName() + ":");
         pw.increaseIndent();
 
-        pw.println("mMeteredMatchCriteria: " + getMatchCriteriaString(mMeteredMatchCriteria));
-        pw.println("mMinEntryUpstreamBandwidthKbps: " + mMinEntryUpstreamBandwidthKbps);
-        pw.println("mMinExitUpstreamBandwidthKbps: " + mMinExitUpstreamBandwidthKbps);
-        pw.println("mMinEntryDownstreamBandwidthKbps: " + mMinEntryDownstreamBandwidthKbps);
-        pw.println("mMinExitDownstreamBandwidthKbps: " + mMinExitDownstreamBandwidthKbps);
+        if (mMeteredMatchCriteria != DEFAULT_METERED_MATCH_CRITERIA) {
+            pw.println("mMeteredMatchCriteria: " + getMatchCriteriaString(mMeteredMatchCriteria));
+        }
+        if (mMinEntryUpstreamBandwidthKbps != DEFAULT_MIN_BANDWIDTH_KBPS) {
+            pw.println("mMinEntryUpstreamBandwidthKbps: " + mMinEntryUpstreamBandwidthKbps);
+        }
+        if (mMinExitUpstreamBandwidthKbps != DEFAULT_MIN_BANDWIDTH_KBPS) {
+            pw.println("mMinExitUpstreamBandwidthKbps: " + mMinExitUpstreamBandwidthKbps);
+        }
+        if (mMinEntryDownstreamBandwidthKbps != DEFAULT_MIN_BANDWIDTH_KBPS) {
+            pw.println("mMinEntryDownstreamBandwidthKbps: " + mMinEntryDownstreamBandwidthKbps);
+        }
+        if (mMinExitDownstreamBandwidthKbps != DEFAULT_MIN_BANDWIDTH_KBPS) {
+            pw.println("mMinExitDownstreamBandwidthKbps: " + mMinExitDownstreamBandwidthKbps);
+        }
         dumpTransportSpecificFields(pw);
 
         pw.decreaseIndent();
diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
index 23a07ab..2544a6d 100644
--- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
@@ -147,7 +147,9 @@
     /** @hide */
     @Override
     void dumpTransportSpecificFields(IndentingPrintWriter pw) {
-        pw.println("mSsids: " + mSsids);
+        if (!mSsids.isEmpty()) {
+            pw.println("mSsids: " + mSsids);
+        }
     }
 
     /**
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 42e6ac4..0b956f8 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1166,7 +1166,7 @@
         /**
          * Tiramisu.
          */
-        public static final int TIRAMISU = CUR_DEVELOPMENT;
+        public static final int TIRAMISU = 33;
     }
 
     /** The type of build, like "user" or "eng". */
diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java
index 0efa34c..bf44d65 100644
--- a/core/java/android/os/IpcDataCache.java
+++ b/core/java/android/os/IpcDataCache.java
@@ -23,7 +23,7 @@
 import android.annotation.TestApi;
 import android.app.PropertyInvalidatedCache;
 import android.text.TextUtils;
-import android.util.Log;
+import android.util.ArraySet;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.FastPrintWriter;
@@ -35,7 +35,6 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Objects;
@@ -324,8 +323,8 @@
     @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
     @TestApi
     public IpcDataCache(int maxEntries, @NonNull @IpcDataCacheModule String module,
-            @NonNull String api,
-            @NonNull String cacheName, @NonNull QueryHandler<Query, Result> computer) {
+            @NonNull String api, @NonNull String cacheName,
+            @NonNull QueryHandler<Query, Result> computer) {
         super(maxEntries, module, api, cacheName, computer);
     }
 
@@ -382,4 +381,210 @@
             @NonNull String api) {
         PropertyInvalidatedCache.invalidateCache(module, api);
     }
+
+    /**
+     * This is a convenience class that encapsulates configuration information for a
+     * cache.  It may be supplied to the cache constructors in lieu of the other
+     * parameters.  The class captures maximum entry count, the module, the key, and the
+     * api.
+     *
+     * There are three specific use cases supported by this class.
+     *
+     * 1. Instance-per-cache: create a static instance of this class using the same
+     *    parameters as would have been given to IpcDataCache (or
+     *    PropertyInvalidatedCache).  This static instance provides a hook for the
+     *    invalidateCache() and disableForLocalProcess() calls, which, generally, must
+     *    also be static.
+     *
+     * 2. Short-hand for shared configuration parameters: create an instance of this class
+     *    to capture the maximum number of entries and the module to be used by more than
+     *    one cache in the class.  Refer to this instance when creating new configs.  Only
+     *    the api and (optionally key) for the new cache must be supplied.
+     *
+     * 3. Tied caches: create a static instance of this class to capture the maximum
+     *    number of entries, the module, and the key.  Refer to this instance when
+     *    creating a new config that differs in only the api.  The new config can be
+     *    created as part of the cache constructor.  All caches that trace back to the
+     *    root config share the same key and are invalidated by the invalidateCache()
+     *    method of the root config.  All caches that trace back to the root config can be
+     *    disabled in the local process by the disableAllForCurrentProcess() method of the
+     *    root config.
+     *
+     * @hide
+     */
+    public static class Config {
+        private final int mMaxEntries;
+        @IpcDataCacheModule
+        private final String mModule;
+        private final String mApi;
+        private final String mName;
+
+        /**
+         * The list of cache names that were created extending this Config.  If
+         * disableForCurrentProcess() is invoked on this config then all children will be
+         * disabled.  Furthermore, any new children based off of this config will be
+         * disabled.  The construction order guarantees that new caches will be disabled
+         * before they are created (the Config must be created before the IpcDataCache is
+         * created).
+         */
+        private ArraySet<String> mChildren;
+
+        /**
+         * True if registered children are disabled in the current process.  If this is
+         * true then all new children are disabled as they are registered.
+         */
+        private boolean mDisabled = false;
+
+        public Config(int maxEntries, @NonNull @IpcDataCacheModule String module,
+                @NonNull String api, @NonNull String name) {
+            mMaxEntries = maxEntries;
+            mModule = module;
+            mApi = api;
+            mName = name;
+        }
+
+        /**
+         * A short-hand constructor that makes the name the same as the api.
+         */
+        public Config(int maxEntries, @NonNull @IpcDataCacheModule String module,
+                @NonNull String api) {
+            this(maxEntries, module, api, api);
+        }
+
+        /**
+         * Copy the module and max entries from the Config and take the api and name from
+         * the parameter list.
+         */
+        public Config(@NonNull Config root, @NonNull String api, @NonNull String name) {
+            this(root.maxEntries(), root.module(), api, name);
+        }
+
+        /**
+         * Copy the module and max entries from the Config and take the api and name from
+         * the parameter list.
+         */
+        public Config(@NonNull Config root, @NonNull String api) {
+            this(root.maxEntries(), root.module(), api, api);
+        }
+
+        /**
+         * Fetch a config that is a child of <this>.  The child shares the same api as the
+         * parent and is registered with the parent for the purposes of disabling in the
+         * current process.
+         */
+        public Config child(@NonNull String name) {
+            final Config result = new Config(this, api(), name);
+            registerChild(name);
+            return result;
+        }
+
+        public final int maxEntries() {
+            return mMaxEntries;
+        }
+
+        @IpcDataCacheModule
+        public final @NonNull String module() {
+            return mModule;
+        }
+
+        public final @NonNull String api() {
+            return mApi;
+        }
+
+        public final @NonNull String name() {
+            return mName;
+        }
+
+        /**
+         * Register a child cache name.  If disableForCurrentProcess() has been called
+         * against this cache, disable th new child.
+         */
+        private final void registerChild(String name) {
+            synchronized (this) {
+                if (mChildren == null) {
+                    mChildren = new ArraySet<>();
+                }
+                mChildren.add(name);
+                if (mDisabled) {
+                    IpcDataCache.disableForCurrentProcess(name);
+                }
+            }
+        }
+
+        /**
+         * Invalidate all caches that share this Config's module and api.
+         */
+        public void invalidateCache() {
+            IpcDataCache.invalidateCache(mModule, mApi);
+        }
+
+        /**
+         * Disable all caches that share this Config's name.
+         */
+        public void disableForCurrentProcess() {
+            IpcDataCache.disableForCurrentProcess(mName);
+        }
+
+        /**
+         * Disable this cache and all children.  Any child that is added in the future
+         * will alwo be disabled.
+         */
+        public void disableAllForCurrentProcess() {
+            synchronized (this) {
+                mDisabled = true;
+                disableForCurrentProcess();
+                if (mChildren != null) {
+                    for (String c : mChildren) {
+                        IpcDataCache.disableForCurrentProcess(c);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Create a new cache using a config.
+     * @hide
+     */
+    public IpcDataCache(@NonNull Config config, @NonNull QueryHandler<Query, Result> computer) {
+        super(config.maxEntries(), config.module(), config.api(), config.name(), computer);
+    }
+
+    /**
+     * An interface suitable for a lambda expression instead of a QueryHandler.
+     * @hide
+     */
+    public interface RemoteCall<Query, Result> {
+        Result apply(Query query) throws RemoteException;
+    }
+
+    /**
+     * This is a query handler that is created with a lambda expression that is invoked
+     * every time the handler is called.  The handler is specifically meant for services
+     * hosted by system_server; the handler automatically rethrows RemoteException as a
+     * RuntimeException, which is the usual handling for failed binder calls.
+     */
+    private static class SystemServerCallHandler<Query, Result>
+            extends IpcDataCache.QueryHandler<Query, Result> {
+        private final RemoteCall<Query, Result> mHandler;
+        public SystemServerCallHandler(RemoteCall handler) {
+            mHandler = handler;
+        }
+        @Override
+        public Result apply(Query query) {
+            try {
+                return mHandler.apply(query);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Create a cache using a config and a lambda expression.
+     * @hide
+     */
+    public IpcDataCache(@NonNull Config config, @NonNull RemoteCall<Query, Result> computer) {
+        this(config, new SystemServerCallHandler<>(computer));
+    }
 }
diff --git a/core/java/android/view/AccessibilityEmbeddedConnection.java b/core/java/android/view/AccessibilityEmbeddedConnection.java
index de895d9..a7d3164 100644
--- a/core/java/android/view/AccessibilityEmbeddedConnection.java
+++ b/core/java/android/view/AccessibilityEmbeddedConnection.java
@@ -33,7 +33,7 @@
  */
 final class AccessibilityEmbeddedConnection extends IAccessibilityEmbeddedConnection.Stub {
     private final WeakReference<ViewRootImpl> mViewRootImpl;
-    private final Matrix mTmpScreenMatrix = new Matrix();
+    private final Matrix mTmpWindowMatrix = new Matrix();
 
     AccessibilityEmbeddedConnection(ViewRootImpl viewRootImpl) {
         mViewRootImpl = new WeakReference<>(viewRootImpl);
@@ -70,14 +70,14 @@
     }
 
     @Override
-    public void setScreenMatrix(float[] matrixValues) {
+    public void setWindowMatrix(float[] matrixValues) {
         final ViewRootImpl viewRootImpl = mViewRootImpl.get();
         if (viewRootImpl != null) {
-            mTmpScreenMatrix.setValues(matrixValues);
-            if (viewRootImpl.mAttachInfo.mScreenMatrixInEmbeddedHierarchy == null) {
-                viewRootImpl.mAttachInfo.mScreenMatrixInEmbeddedHierarchy = new Matrix();
+            mTmpWindowMatrix.setValues(matrixValues);
+            if (viewRootImpl.mAttachInfo.mWindowMatrixInEmbeddedHierarchy == null) {
+                viewRootImpl.mAttachInfo.mWindowMatrixInEmbeddedHierarchy = new Matrix();
             }
-            viewRootImpl.mAttachInfo.mScreenMatrixInEmbeddedHierarchy.set(mTmpScreenMatrix);
+            viewRootImpl.mAttachInfo.mWindowMatrixInEmbeddedHierarchy.set(mTmpWindowMatrix);
         }
     }
 }
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 7e0b794..23e1505 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -22,7 +22,6 @@
 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
 
 import android.graphics.Matrix;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
@@ -112,10 +111,7 @@
 
     private final ArrayList<View> mTempArrayList = new ArrayList<View>();
 
-    private final Point mTempPoint = new Point();
     private final Rect mTempRect = new Rect();
-    private final Rect mTempRect1 = new Rect();
-    private final Rect mTempRect2 = new Rect();
     private final RectF mTempRectF = new RectF();
 
     private AddNodeInfosForViewId mAddNodeInfosForViewId;
@@ -131,7 +127,7 @@
     private int mActiveRequestPreparerId;
 
     public AccessibilityInteractionController(ViewRootImpl viewRootImpl) {
-        Looper looper =  viewRootImpl.mHandler.getLooper();
+        Looper looper = viewRootImpl.mHandler.getLooper();
         mMyLooperThreadId = looper.getThread().getId();
         mMyProcessId = Process.myPid();
         mHandler = new PrivateHandler(looper);
@@ -173,7 +169,8 @@
     public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
             long accessibilityNodeId, Region interactiveRegion, int interactionId,
             IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
-            long interrogatingTid, MagnificationSpec spec, Bundle arguments) {
+            long interrogatingTid, MagnificationSpec spec, float[] matrixValues,
+            Bundle arguments) {
         final Message message = mHandler.obtainMessage();
         message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID;
         message.arg1 = flags;
@@ -186,6 +183,7 @@
         args.arg2 = spec;
         args.arg3 = interactiveRegion;
         args.arg4 = arguments;
+        args.arg5 = matrixValues;
         message.obj = args;
 
         synchronized (mLock) {
@@ -344,6 +342,7 @@
         final MagnificationSpec spec = (MagnificationSpec) args.arg2;
         final Region interactiveRegion = (Region) args.arg3;
         final Bundle arguments = (Bundle) args.arg4;
+        final float[] matrixValues = (float[]) args.arg5;
 
         args.recycle();
 
@@ -378,7 +377,7 @@
             if (!interruptPrefetch) {
                 // Return found node and prefetched nodes in one IPC.
                 updateInfosForViewportAndReturnFindNodeResult(infos, callback, interactionId, spec,
-                        interactiveRegion);
+                        matrixValues, interactiveRegion);
 
                 final SatisfiedFindAccessibilityNodeByAccessibilityIdRequest satisfiedRequest =
                         getSatisfiedRequestInPrefetch(requestedNode == null ? null : requestedNode,
@@ -391,13 +390,13 @@
                 // Return found node.
                 updateInfoForViewportAndReturnFindNodeResult(
                         requestedNode == null ? null : new AccessibilityNodeInfo(requestedNode),
-                        callback, interactionId, spec, interactiveRegion);
+                        callback, interactionId, spec, matrixValues, interactiveRegion);
             }
         }
         mPrefetcher.prefetchAccessibilityNodeInfos(requestedView,
                 requestedNode == null ? null : new AccessibilityNodeInfo(requestedNode), infos);
         mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
-        updateInfosForViewPort(infos, spec, interactiveRegion);
+        updateInfosForViewPort(infos, spec, matrixValues, interactiveRegion);
         final SatisfiedFindAccessibilityNodeByAccessibilityIdRequest satisfiedRequest =
                 getSatisfiedRequestInPrefetch(requestedNode == null ? null : requestedNode, infos,
                         flags);
@@ -439,7 +438,7 @@
     public void findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId,
             String viewId, Region interactiveRegion, int interactionId,
             IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
-            long interrogatingTid, MagnificationSpec spec) {
+            long interrogatingTid, MagnificationSpec spec, float[] matrixValues) {
         Message message = mHandler.obtainMessage();
         message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID;
         message.arg1 = flags;
@@ -451,6 +450,7 @@
         args.arg2 = spec;
         args.arg3 = viewId;
         args.arg4 = interactiveRegion;
+        args.arg5 = matrixValues;
         message.obj = args;
 
         scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
@@ -467,6 +467,7 @@
         final MagnificationSpec spec = (MagnificationSpec) args.arg2;
         final String viewId = (String) args.arg3;
         final Region interactiveRegion = (Region) args.arg4;
+        final float[] matrixValues = (float[]) args.arg5;
         args.recycle();
 
         final List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
@@ -494,14 +495,14 @@
         } finally {
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
             updateInfosForViewportAndReturnFindNodeResult(
-                    infos, callback, interactionId, spec, interactiveRegion);
+                    infos, callback, interactionId, spec, matrixValues, interactiveRegion);
         }
     }
 
     public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId,
             String text, Region interactiveRegion, int interactionId,
             IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
-            long interrogatingTid, MagnificationSpec spec) {
+            long interrogatingTid, MagnificationSpec spec, float[] matrixValues) {
         Message message = mHandler.obtainMessage();
         message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT;
         message.arg1 = flags;
@@ -514,6 +515,7 @@
         args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
         args.argi3 = interactionId;
         args.arg4 = interactiveRegion;
+        args.arg5 = matrixValues;
         message.obj = args;
 
         scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
@@ -531,6 +533,7 @@
         final int virtualDescendantId = args.argi2;
         final int interactionId = args.argi3;
         final Region interactiveRegion = (Region) args.arg4;
+        final float[] matrixValues = (float[]) args.arg5;
         args.recycle();
 
         List<AccessibilityNodeInfo> infos = null;
@@ -577,14 +580,14 @@
         } finally {
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
             updateInfosForViewportAndReturnFindNodeResult(
-                    infos, callback, interactionId, spec, interactiveRegion);
+                    infos, callback, interactionId, spec, matrixValues, interactiveRegion);
         }
     }
 
     public void findFocusClientThread(long accessibilityNodeId, int focusType,
             Region interactiveRegion, int interactionId,
             IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
-            long interrogatingTid, MagnificationSpec spec) {
+            long interrogatingTid, MagnificationSpec spec, float[] matrixValues) {
         Message message = mHandler.obtainMessage();
         message.what = PrivateHandler.MSG_FIND_FOCUS;
         message.arg1 = flags;
@@ -597,7 +600,7 @@
         args.arg1 = callback;
         args.arg2 = spec;
         args.arg3 = interactiveRegion;
-
+        args.arg4 = matrixValues;
         message.obj = args;
 
         scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
@@ -615,6 +618,7 @@
             (IAccessibilityInteractionConnectionCallback) args.arg1;
         final MagnificationSpec spec = (MagnificationSpec) args.arg2;
         final Region interactiveRegion = (Region) args.arg3;
+        final float[] matrixValues = (float[]) args.arg4;
         args.recycle();
 
         AccessibilityNodeInfo focused = null;
@@ -672,14 +676,14 @@
         } finally {
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
             updateInfoForViewportAndReturnFindNodeResult(
-                    focused, callback, interactionId, spec, interactiveRegion);
+                    focused, callback, interactionId, spec, matrixValues, interactiveRegion);
         }
     }
 
     public void focusSearchClientThread(long accessibilityNodeId, int direction,
             Region interactiveRegion, int interactionId,
             IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
-            long interrogatingTid, MagnificationSpec spec) {
+            long interrogatingTid, MagnificationSpec spec, float[] matrixValues) {
         Message message = mHandler.obtainMessage();
         message.what = PrivateHandler.MSG_FOCUS_SEARCH;
         message.arg1 = flags;
@@ -691,6 +695,7 @@
         args.arg1 = callback;
         args.arg2 = spec;
         args.arg3 = interactiveRegion;
+        args.arg4 = matrixValues;
 
         message.obj = args;
 
@@ -708,7 +713,7 @@
             (IAccessibilityInteractionConnectionCallback) args.arg1;
         final MagnificationSpec spec = (MagnificationSpec) args.arg2;
         final Region interactiveRegion = (Region) args.arg3;
-
+        final float[] matrixValues = (float[]) args.arg4;
         args.recycle();
 
         AccessibilityNodeInfo next = null;
@@ -727,7 +732,7 @@
         } finally {
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
             updateInfoForViewportAndReturnFindNodeResult(
-                    next, callback, interactionId, spec, interactiveRegion);
+                    next, callback, interactionId, spec, matrixValues, interactiveRegion);
         }
     }
 
@@ -882,13 +887,20 @@
         }
     }
 
+    // The boundInScreen includes magnification effect, so we need to normalize it before
+    // determine the visibility.
     private void adjustIsVisibleToUserIfNeeded(AccessibilityNodeInfo info,
-            Region interactiveRegion) {
+            Region interactiveRegion, MagnificationSpec spec) {
         if (interactiveRegion == null || info == null) {
             return;
         }
         Rect boundsInScreen = mTempRect;
         info.getBoundsInScreen(boundsInScreen);
+        if (spec != null && !spec.isNop()) {
+            boundsInScreen.offset((int) -spec.offsetX, (int) -spec.offsetY);
+            boundsInScreen.scale(1 / spec.scale);
+        }
+
         if (interactiveRegion.quickReject(boundsInScreen) && !shouldBypassAdjustIsVisible()) {
             info.setVisibleToUser(false);
         }
@@ -902,36 +914,30 @@
         return false;
     }
 
-    private void applyScreenMatrixIfNeeded(List<AccessibilityNodeInfo> infos) {
-        if (infos == null || shouldBypassApplyScreenMatrix()) {
-            return;
-        }
-        final int infoCount = infos.size();
-        for (int i = 0; i < infoCount; i++) {
-            final AccessibilityNodeInfo info = infos.get(i);
-            applyScreenMatrixIfNeeded(info);
-        }
-    }
-
-    private void applyScreenMatrixIfNeeded(AccessibilityNodeInfo info) {
-        if (info == null || shouldBypassApplyScreenMatrix()) {
+    /**
+     * Applies the host-window matrix to the embedded node. After this transform, The node bounds
+     *  will be transformed from embedded window coordinates to host-window coordinates.
+     *
+     */
+    private void applyHostWindowMatrixIfNeeded(AccessibilityNodeInfo info) {
+        if (info == null || shouldBypassApplyWindowMatrix()) {
             return;
         }
         final Rect boundsInScreen = mTempRect;
         final RectF transformedBounds = mTempRectF;
-        final Matrix screenMatrix = mViewRootImpl.mAttachInfo.mScreenMatrixInEmbeddedHierarchy;
+        final Matrix windowMatrix = mViewRootImpl.mAttachInfo.mWindowMatrixInEmbeddedHierarchy;
 
         info.getBoundsInScreen(boundsInScreen);
         transformedBounds.set(boundsInScreen);
-        screenMatrix.mapRect(transformedBounds);
+        windowMatrix.mapRect(transformedBounds);
         boundsInScreen.set((int) transformedBounds.left, (int) transformedBounds.top,
                 (int) transformedBounds.right, (int) transformedBounds.bottom);
         info.setBoundsInScreen(boundsInScreen);
     }
 
-    private boolean shouldBypassApplyScreenMatrix() {
-        final Matrix screenMatrix = mViewRootImpl.mAttachInfo.mScreenMatrixInEmbeddedHierarchy;
-        return screenMatrix == null || screenMatrix.isIdentity();
+    private boolean shouldBypassApplyWindowMatrix() {
+        final Matrix windowMatrix = mViewRootImpl.mAttachInfo.mWindowMatrixInEmbeddedHierarchy;
+        return windowMatrix == null || windowMatrix.isIdentity();
     }
 
     private void associateLeashedParentIfNeeded(AccessibilityNodeInfo info) {
@@ -963,46 +969,17 @@
         if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
             return;
         }
-
         Rect boundsInParent = mTempRect;
-        Rect boundsInScreen = mTempRect1;
 
         info.getBoundsInParent(boundsInParent);
-        info.getBoundsInScreen(boundsInScreen);
         if (applicationScale != 1.0f) {
             boundsInParent.scale(applicationScale);
-            boundsInScreen.scale(applicationScale);
         }
         if (spec != null) {
             boundsInParent.scale(spec.scale);
             // boundsInParent must not be offset.
-            boundsInScreen.scale(spec.scale);
-            boundsInScreen.offset((int) spec.offsetX, (int) spec.offsetY);
         }
         info.setBoundsInParent(boundsInParent);
-        info.setBoundsInScreen(boundsInScreen);
-
-        // Scale text locations if they are present
-        if (info.hasExtras()) {
-            Bundle extras = info.getExtras();
-            Parcelable[] textLocations =
-                    extras.getParcelableArray(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY);
-            if (textLocations != null) {
-                for (int i = 0; i < textLocations.length; i++) {
-                    // Unchecked cast - an app that puts other objects in this bundle with this
-                    // key will crash.
-                    RectF textLocation = ((RectF) textLocations[i]);
-                    if (textLocation == null) {
-                        continue;
-                    }
-                    textLocation.scale(applicationScale);
-                    if (spec != null) {
-                        textLocation.scale(spec.scale);
-                        textLocation.offset(spec.offsetX, spec.offsetY);
-                    }
-                }
-            }
-        }
     }
 
     private boolean shouldApplyAppScaleAndMagnificationSpec(float appScale,
@@ -1011,27 +988,88 @@
     }
 
     private void updateInfosForViewPort(List<AccessibilityNodeInfo> infos, MagnificationSpec spec,
-                                        Region interactiveRegion) {
+            float[] matrixValues, Region interactiveRegion) {
         for (int i = 0; i < infos.size(); i++) {
-            updateInfoForViewPort(infos.get(i), spec, interactiveRegion);
+            updateInfoForViewPort(infos.get(i), spec, matrixValues, interactiveRegion);
         }
     }
 
     private void updateInfoForViewPort(AccessibilityNodeInfo info, MagnificationSpec spec,
-                                       Region interactiveRegion) {
+            float[] matrixValues, Region interactiveRegion) {
         associateLeashedParentIfNeeded(info);
-        applyScreenMatrixIfNeeded(info);
-        // To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node,
-        // then impact the visibility result, we need to adjust visibility before apply scale.
-        adjustIsVisibleToUserIfNeeded(info, interactiveRegion);
+
+        applyHostWindowMatrixIfNeeded(info);
+        // Transform view bounds from window coordinates to screen coordinates.
+        transformBoundsWithScreenMatrix(info, matrixValues);
+        adjustIsVisibleToUserIfNeeded(info, interactiveRegion, spec);
         applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
     }
 
+
+    /**
+     * Transforms the regions from local screen coordinate to global screen coordinate with the
+     * given transform matrix used in on-screen coordinate.
+     *
+     * @param info the AccessibilityNodeInfo that has the region in application screen coordinate
+     * @param matrixValues the matrix to be applied
+     */
+    private void transformBoundsWithScreenMatrix(AccessibilityNodeInfo info,
+            float[] matrixValues) {
+        if (info == null || matrixValues == null) {
+            return;
+        }
+        final Rect boundInScreen = mTempRect;
+        final RectF transformedBounds = mTempRectF;
+
+        info.getBoundsInScreen(boundInScreen);
+        transformedBounds.set(boundInScreen);
+
+        final Matrix transformMatrix = new Matrix();
+        transformMatrix.setValues(matrixValues);
+        final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
+        if (applicationScale != 1f) {
+            transformMatrix.preScale(applicationScale, applicationScale);
+        }
+        // Transform the bounds from application screen coordinates to global window coordinates.
+        // For the embedded node, the bounds we get is already in window coordinates, so we don't
+        // need to do it.
+        if (mViewRootImpl.mAttachInfo.mWindowMatrixInEmbeddedHierarchy == null) {
+            transformMatrix.preTranslate(-mViewRootImpl.mAttachInfo.mWindowLeft,
+                    -mViewRootImpl.mAttachInfo.mWindowTop);
+        }
+
+        if (transformMatrix.isIdentity()) {
+            return;
+        }
+        transformMatrix.mapRect(transformedBounds);
+        // Offset 0.5f to round after casting.
+        transformedBounds.offset(0.5f, 0.5f);
+        boundInScreen.set((int) (transformedBounds.left), (int) transformedBounds.top,
+                (int) transformedBounds.right, (int) transformedBounds.bottom);
+        info.setBoundsInScreen(boundInScreen);
+        // Scale text locations if they are present
+        if (info.hasExtras()) {
+            final Bundle extras = info.getExtras();
+            final RectF[] textLocations =
+                    extras.getParcelableArray(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY, RectF.class);
+            if (textLocations != null) {
+                for (int i = 0; i < textLocations.length; i++) {
+                    // Unchecked cast - an app that puts other objects in this bundle with this
+                    // key will crash.
+                    final RectF textLocation = textLocations[i];
+                    if (textLocation != null) {
+                        transformMatrix.mapRect(textLocation);
+                    }
+                }
+            }
+        }
+    }
+
     private void updateInfosForViewportAndReturnFindNodeResult(List<AccessibilityNodeInfo> infos,
             IAccessibilityInteractionConnectionCallback callback, int interactionId,
-            MagnificationSpec spec, Region interactiveRegion) {
+            MagnificationSpec spec, float[] matrixValues, Region interactiveRegion) {
         if (infos != null) {
-            updateInfosForViewPort(infos, spec, interactiveRegion);
+            updateInfosForViewPort(infos, spec, matrixValues, interactiveRegion);
         }
         returnFindNodesResult(infos, callback, interactionId);
     }
@@ -1141,8 +1179,8 @@
 
     private void updateInfoForViewportAndReturnFindNodeResult(AccessibilityNodeInfo info,
             IAccessibilityInteractionConnectionCallback callback, int interactionId,
-            MagnificationSpec spec, Region interactiveRegion) {
-        updateInfoForViewPort(info, spec, interactiveRegion);
+            MagnificationSpec spec, float[] matrixValues, Region interactiveRegion) {
+        updateInfoForViewPort(info, spec, matrixValues, interactiveRegion);
         returnFindNodeResult(info, callback, interactionId);
     }
 
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 7b6a0d6..cce3e8c 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -833,10 +833,12 @@
     }
 
     /**
-     * @see InsetsState#calculateVisibleInsets(Rect, int)
+     * @see InsetsState#calculateVisibleInsets(Rect, int, int, int, int)
      */
-    public Insets calculateVisibleInsets(@SoftInputModeFlags int softInputMode) {
-        return mState.calculateVisibleInsets(mFrame, softInputMode);
+    public Insets calculateVisibleInsets(int windowType, int windowingMode,
+            @SoftInputModeFlags int softInputMode, int windowFlags) {
+        return mState.calculateVisibleInsets(mFrame, windowType, windowingMode, softInputMode,
+                windowFlags);
     }
 
     /**
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index b1b630e..eb74608 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -23,11 +23,11 @@
 import static android.view.WindowInsets.Type.displayCutout;
 import static android.view.WindowInsets.Type.ime;
 import static android.view.WindowInsets.Type.indexOf;
-import static android.view.WindowInsets.Type.isVisibleInsetsType;
 import static android.view.WindowInsets.Type.statusBars;
 import static android.view.WindowInsets.Type.systemBars;
 import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
@@ -257,7 +257,7 @@
         if ((legacyWindowFlags & FLAG_FULLSCREEN) != 0) {
             compatInsetsTypes &= ~statusBars();
         }
-        if (clearCompatInsets(windowType, legacyWindowFlags, windowingMode)) {
+        if (clearsCompatInsets(windowType, legacyWindowFlags, windowingMode)) {
             compatInsetsTypes = 0;
         }
 
@@ -358,17 +358,23 @@
         return insets;
     }
 
-    public Insets calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode) {
+    public Insets calculateVisibleInsets(Rect frame, int windowType, int windowingMode,
+            @SoftInputModeFlags int softInputMode, int windowFlags) {
+        if (clearsCompatInsets(windowType, windowFlags, windowingMode)) {
+            return Insets.NONE;
+        }
+        final int softInputAdjustMode = softInputMode & SOFT_INPUT_MASK_ADJUST;
+        final int visibleInsetsTypes = softInputAdjustMode != SOFT_INPUT_ADJUST_NOTHING
+                ? systemBars() | ime()
+                : systemBars();
         Insets insets = Insets.NONE;
         for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
             InsetsSource source = mSources[type];
             if (source == null) {
                 continue;
             }
-
-            // Ignore everything that's not a system bar or IME.
-            int publicType = InsetsState.toPublicType(type);
-            if (!isVisibleInsetsType(publicType, softInputMode)) {
+            final int publicType = InsetsState.toPublicType(type);
+            if ((publicType & visibleInsetsTypes) == 0) {
                 continue;
             }
             insets = Insets.max(source.calculateVisibleInsets(frame), insets);
@@ -676,7 +682,7 @@
         mSources[source.getType()] = source;
     }
 
-    public static boolean clearCompatInsets(int windowType, int windowFlags, int windowingMode) {
+    public static boolean clearsCompatInsets(int windowType, int windowFlags, int windowingMode) {
         return (windowFlags & FLAG_LAYOUT_NO_LIMITS) != 0
                 && windowType != TYPE_WALLPAPER && windowType != TYPE_SYSTEM_ERROR
                 && !WindowConfiguration.inMultiWindowMode(windowingMode);
diff --git a/core/java/android/view/RemoteAccessibilityController.java b/core/java/android/view/RemoteAccessibilityController.java
index bc0fab1b..8855fb5 100644
--- a/core/java/android/view/RemoteAccessibilityController.java
+++ b/core/java/android/view/RemoteAccessibilityController.java
@@ -28,7 +28,7 @@
     private static final String TAG = "RemoteAccessibilityController";
     private int mHostId;
     private RemoteAccessibilityEmbeddedConnection mConnectionWrapper;
-    private Matrix mScreenMatrixForEmbeddedHierarchy = new Matrix();
+    private Matrix mWindowMatrixForEmbeddedHierarchy = new Matrix();
     private final float[] mMatrixValues = new float[9];
     private View mHostView;
 
@@ -140,9 +140,9 @@
         return mConnectionWrapper;
     }
 
-    void setScreenMatrix(Matrix m) {
-        // If the screen matrix is identity or doesn't change, do nothing.
-        if (m.isIdentity() || m.equals(mScreenMatrixForEmbeddedHierarchy)) {
+    void setWindowMatrix(Matrix m) {
+        // If the window matrix is identity or doesn't change, do nothing.
+        if (m.isIdentity() || m.equals(mWindowMatrixForEmbeddedHierarchy)) {
             return;
         }
 
@@ -153,8 +153,8 @@
                 return;
             }
             m.getValues(mMatrixValues);
-            wrapper.getConnection().setScreenMatrix(mMatrixValues);
-            mScreenMatrixForEmbeddedHierarchy.set(m);
+            wrapper.getConnection().setWindowMatrix(mMatrixValues);
+            mWindowMatrixForEmbeddedHierarchy.set(m);
         } catch (RemoteException e) {
             Log.d(TAG, "Error while setScreenMatrix " + e);
         }
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index ed57136..017f2e4 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1781,11 +1781,16 @@
             return;
         }
         getBoundsOnScreen(mTmpRect);
+
+        // To compute the node bounds of the node on the embedded window,
+        // apply this matrix to get the bounds in host window-relative coordinates,
+        // then using the global transform to get the actual bounds on screen.
+        mTmpRect.offset(-mAttachInfo.mWindowLeft, -mAttachInfo.mWindowTop);
         mTmpMatrix.reset();
         mTmpMatrix.setTranslate(mTmpRect.left, mTmpRect.top);
         mTmpMatrix.postScale(mScreenRect.width() / (float) mSurfaceWidth,
                 mScreenRect.height() / (float) mSurfaceHeight);
-        mRemoteAccessibilityController.setScreenMatrix(mTmpMatrix);
+        mRemoteAccessibilityController.setWindowMatrix(mTmpMatrix);
     }
 
     @Override
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8b9a86b9..569461e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -29938,10 +29938,10 @@
         boolean mHandlingPointerEvent;
 
         /**
-         * The screen matrix of this view when it's on a {@link SurfaceControlViewHost} that is
+         * The window matrix of this view when it's on a {@link SurfaceControlViewHost} that is
          * embedded within a SurfaceView.
          */
-        Matrix mScreenMatrixInEmbeddedHierarchy;
+        Matrix mWindowMatrixInEmbeddedHierarchy;
 
         /**
          * Global to the view hierarchy used as a temporary for dealing with
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a3d0bf7..92289d9 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2570,7 +2570,8 @@
             mAttachInfo.mContentInsets.set(mLastWindowInsets.getSystemWindowInsets().toRect());
             mAttachInfo.mStableInsets.set(mLastWindowInsets.getStableInsets().toRect());
             mAttachInfo.mVisibleInsets.set(mInsetsController.calculateVisibleInsets(
-                    mWindowAttributes.softInputMode).toRect());
+                    mWindowAttributes.type, config.windowConfiguration.getWindowingMode(),
+                    mWindowAttributes.softInputMode, mWindowAttributes.flags).toRect());
         }
         return mLastWindowInsets;
     }
@@ -10249,13 +10250,14 @@
         public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
                 Region interactiveRegion, int interactionId,
                 IAccessibilityInteractionConnectionCallback callback, int flags,
-                int interrogatingPid, long interrogatingTid, MagnificationSpec spec, Bundle args) {
+                int interrogatingPid, long interrogatingTid, MagnificationSpec spec, float[] matrix,
+                Bundle args) {
             ViewRootImpl viewRootImpl = mViewRootImpl.get();
             if (viewRootImpl != null && viewRootImpl.mView != null) {
                 viewRootImpl.getAccessibilityInteractionController()
                     .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityNodeId,
                             interactiveRegion, interactionId, callback, flags, interrogatingPid,
-                            interrogatingTid, spec, args);
+                            interrogatingTid, spec, matrix, args);
             } else {
                 // We cannot make the call and notify the caller so it does not wait.
                 try {
@@ -10290,13 +10292,14 @@
         public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId,
                 String viewId, Region interactiveRegion, int interactionId,
                 IAccessibilityInteractionConnectionCallback callback, int flags,
-                int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
+                int interrogatingPid, long interrogatingTid, MagnificationSpec spec,
+                float[] matrix) {
             ViewRootImpl viewRootImpl = mViewRootImpl.get();
             if (viewRootImpl != null && viewRootImpl.mView != null) {
                 viewRootImpl.getAccessibilityInteractionController()
                     .findAccessibilityNodeInfosByViewIdClientThread(accessibilityNodeId,
                             viewId, interactiveRegion, interactionId, callback, flags,
-                            interrogatingPid, interrogatingTid, spec);
+                            interrogatingPid, interrogatingTid, spec, matrix);
             } else {
                 // We cannot make the call and notify the caller so it does not wait.
                 try {
@@ -10311,13 +10314,14 @@
         public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
                 Region interactiveRegion, int interactionId,
                 IAccessibilityInteractionConnectionCallback callback, int flags,
-                int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
+                int interrogatingPid, long interrogatingTid, MagnificationSpec spec,
+                float[] matrix) {
             ViewRootImpl viewRootImpl = mViewRootImpl.get();
             if (viewRootImpl != null && viewRootImpl.mView != null) {
                 viewRootImpl.getAccessibilityInteractionController()
                     .findAccessibilityNodeInfosByTextClientThread(accessibilityNodeId, text,
                             interactiveRegion, interactionId, callback, flags, interrogatingPid,
-                            interrogatingTid, spec);
+                            interrogatingTid, spec, matrix);
             } else {
                 // We cannot make the call and notify the caller so it does not wait.
                 try {
@@ -10331,13 +10335,14 @@
         @Override
         public void findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion,
                 int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
-                int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
+                int interrogatingPid, long interrogatingTid, MagnificationSpec spec,
+                float[] matrix) {
             ViewRootImpl viewRootImpl = mViewRootImpl.get();
             if (viewRootImpl != null && viewRootImpl.mView != null) {
                 viewRootImpl.getAccessibilityInteractionController()
                     .findFocusClientThread(accessibilityNodeId, focusType, interactiveRegion,
                             interactionId, callback, flags, interrogatingPid, interrogatingTid,
-                            spec);
+                            spec, matrix);
             } else {
                 // We cannot make the call and notify the caller so it does not wait.
                 try {
@@ -10351,13 +10356,14 @@
         @Override
         public void focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion,
                 int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
-                int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
+                int interrogatingPid, long interrogatingTid, MagnificationSpec spec,
+                float[] matrix) {
             ViewRootImpl viewRootImpl = mViewRootImpl.get();
             if (viewRootImpl != null && viewRootImpl.mView != null) {
                 viewRootImpl.getAccessibilityInteractionController()
                     .focusSearchClientThread(accessibilityNodeId, direction, interactiveRegion,
                             interactionId, callback, flags, interrogatingPid, interrogatingTid,
-                            spec);
+                            spec, matrix);
             } else {
                 // We cannot make the call and notify the caller so it does not wait.
                 try {
diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java
index 1edbbba..a5b8720 100644
--- a/core/java/android/view/WindowInfo.java
+++ b/core/java/android/view/WindowInfo.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.app.ActivityTaskManager;
+import android.graphics.Matrix;
 import android.graphics.Region;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -53,6 +54,11 @@
     public boolean hasFlagWatchOutsideTouch;
     public int displayId = Display.INVALID_DISPLAY;
     public int taskId = ActivityTaskManager.INVALID_TASK_ID;
+    // The matrix applied to the bounds in window coordinate to get the corresponding values in
+    // screen coordinates.
+    public float[] mTransformMatrix = new float[9];
+
+    public MagnificationSpec mMagnificationSpec = new MagnificationSpec();
 
     private WindowInfo() {
         /* do nothing - hide constructor */
@@ -81,6 +87,9 @@
         window.accessibilityIdOfAnchor = other.accessibilityIdOfAnchor;
         window.inPictureInPicture = other.inPictureInPicture;
         window.hasFlagWatchOutsideTouch = other.hasFlagWatchOutsideTouch;
+        for (int i = 0; i < window.mTransformMatrix.length; i++) {
+            window.mTransformMatrix[i] = other.mTransformMatrix[i];
+        }
 
         if (other.childTokens != null && !other.childTokens.isEmpty()) {
             if (window.childTokens == null) {
@@ -89,7 +98,7 @@
                 window.childTokens.addAll(other.childTokens);
             }
         }
-
+        window.mMagnificationSpec.setTo(other.mMagnificationSpec);
         return window;
     }
 
@@ -118,6 +127,7 @@
         parcel.writeLong(accessibilityIdOfAnchor);
         parcel.writeInt(inPictureInPicture ? 1 : 0);
         parcel.writeInt(hasFlagWatchOutsideTouch ? 1 : 0);
+        parcel.writeFloatArray(mTransformMatrix);
 
         if (childTokens != null && !childTokens.isEmpty()) {
             parcel.writeInt(1);
@@ -125,6 +135,7 @@
         } else {
             parcel.writeInt(0);
         }
+        mMagnificationSpec.writeToParcel(parcel, flags);
     }
 
     @Override
@@ -145,6 +156,10 @@
         builder.append(", accessibility anchor=").append(accessibilityIdOfAnchor);
         builder.append(", pictureInPicture=").append(inPictureInPicture);
         builder.append(", watchOutsideTouch=").append(hasFlagWatchOutsideTouch);
+        Matrix matrix = new Matrix();
+        matrix.setValues(mTransformMatrix);
+        builder.append(", mTransformMatrix=").append(matrix);
+        builder.append(", mMagnificationSpec=").append(mMagnificationSpec);
         builder.append(']');
         return builder.toString();
     }
@@ -163,7 +178,7 @@
         accessibilityIdOfAnchor = parcel.readLong();
         inPictureInPicture = (parcel.readInt() == 1);
         hasFlagWatchOutsideTouch = (parcel.readInt() == 1);
-
+        parcel.readFloatArray(mTransformMatrix);
         final boolean hasChildren = (parcel.readInt() == 1);
         if (hasChildren) {
             if (childTokens == null) {
@@ -171,6 +186,7 @@
             }
             parcel.readBinderList(childTokens);
         }
+        mMagnificationSpec = MagnificationSpec.CREATOR.createFromParcel(parcel);
     }
 
     private void clear() {
@@ -188,6 +204,10 @@
         }
         inPictureInPicture = false;
         hasFlagWatchOutsideTouch = false;
+        for (int i = 0; i < mTransformMatrix.length; i++) {
+            mTransformMatrix[i] = 0;
+        }
+        mMagnificationSpec.clear();
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<WindowInfo> CREATOR =
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 1c6d93d..c846175 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -32,8 +32,6 @@
 import static android.view.WindowInsets.Type.ime;
 import static android.view.WindowInsets.Type.indexOf;
 import static android.view.WindowInsets.Type.systemBars;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
 
 import android.annotation.IntDef;
 import android.annotation.IntRange;
@@ -46,7 +44,6 @@
 import android.util.SparseArray;
 import android.view.View.OnApplyWindowInsetsListener;
 import android.view.WindowInsets.Type.InsetsType;
-import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputMethod;
 
@@ -1600,17 +1597,6 @@
         public static @InsetsType int all() {
             return 0xFFFFFFFF;
         }
-
-        /**
-         * Checks whether the specified type is considered to be part of visible insets.
-         * @hide
-         */
-        public static boolean isVisibleInsetsType(int type,
-                @SoftInputModeFlags int softInputModeFlags) {
-            int softInputMode = softInputModeFlags & SOFT_INPUT_MASK_ADJUST;
-            return (type & Type.systemBars()) != 0
-                    || (softInputMode != SOFT_INPUT_ADJUST_NOTHING && (type & Type.ime()) != 0);
-        }
     }
 
     /**
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 8e9f9d9..cfe44bb 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3590,12 +3590,13 @@
 
         /**
          * If specified, the insets provided by this window will be our window frame minus the
-         * insets specified by providedInternalInsets. This should not be used together with
-         * {@link WindowState#mGivenContentInsets}. If both of them are set, both will be applied.
+         * insets specified by providedInternalInsets for each type. This should not be used
+         * together with {@link WindowState#mGivenContentInsets}. If both of them are set, both will
+         * be applied.
          *
          * @hide
          */
-        public Insets providedInternalInsets = Insets.NONE;
+        public Insets[] providedInternalInsets;
 
         /**
          * If specified, the insets provided by this window for the IME will be our window frame
@@ -3603,7 +3604,7 @@
          *
          * @hide
          */
-        public Insets providedInternalImeInsets = Insets.NONE;
+        public Insets[] providedInternalImeInsets;
 
         /**
          * If specified, the frame that used to calculate relative {@link RoundedCorner} will be
@@ -3989,8 +3990,18 @@
             } else {
                 out.writeInt(0);
             }
-            providedInternalInsets.writeToParcel(out, 0 /* parcelableFlags */);
-            providedInternalImeInsets.writeToParcel(out, 0 /* parcelableFlags */);
+            if (providedInternalInsets != null) {
+                out.writeInt(providedInternalInsets.length);
+                out.writeTypedArray(providedInternalInsets, 0 /* parcelableFlags */);
+            } else {
+                out.writeInt(0);
+            }
+            if (providedInternalImeInsets != null) {
+                out.writeInt(providedInternalImeInsets.length);
+                out.writeTypedArray(providedInternalImeInsets, 0 /* parcelableFlags */);
+            } else {
+                out.writeInt(0);
+            }
             out.writeBoolean(insetsRoundedCornerFrame);
             if (paramsForRotation != null) {
                 checkNonRecursiveParams();
@@ -4070,8 +4081,16 @@
                 providesInsetsTypes = new int[insetsTypesLength];
                 in.readIntArray(providesInsetsTypes);
             }
-            providedInternalInsets = Insets.CREATOR.createFromParcel(in);
-            providedInternalImeInsets = Insets.CREATOR.createFromParcel(in);
+            int providedInternalInsetsLength = in.readInt();
+            if (providedInternalInsetsLength > 0) {
+                providedInternalInsets = new Insets[providedInternalInsetsLength];
+                in.readTypedArray(providedInternalInsets, Insets.CREATOR);
+            }
+            int providedInternalImeInsetsLength = in.readInt();
+            if (providedInternalImeInsetsLength > 0) {
+                providedInternalImeInsets = new Insets[providedInternalImeInsetsLength];
+                in.readTypedArray(providedInternalImeInsets, Insets.CREATOR);
+            }
             insetsRoundedCornerFrame = in.readBoolean();
             int paramsForRotationLength = in.readInt();
             if (paramsForRotationLength > 0) {
@@ -4374,12 +4393,12 @@
                 changes |= LAYOUT_CHANGED;
             }
 
-            if (!providedInternalInsets.equals(o.providedInternalInsets)) {
+            if (!Arrays.equals(providedInternalInsets, o.providedInternalInsets)) {
                 providedInternalInsets = o.providedInternalInsets;
                 changes |= LAYOUT_CHANGED;
             }
 
-            if (!providedInternalImeInsets.equals(o.providedInternalImeInsets)) {
+            if (!Arrays.equals(providedInternalImeInsets, o.providedInternalImeInsets)) {
                 providedInternalImeInsets = o.providedInternalImeInsets;
                 changes |= LAYOUT_CHANGED;
             }
@@ -4590,13 +4609,21 @@
                     sb.append(InsetsState.typeToString(providesInsetsTypes[i]));
                 }
             }
-            if (!providedInternalInsets.equals(Insets.NONE)) {
+            if (providedInternalInsets != null) {
+                sb.append(System.lineSeparator());
                 sb.append(" providedInternalInsets=");
-                sb.append(providedInternalInsets);
+                for (int i = 0; i < providedInternalInsets.length; ++i) {
+                    if (i > 0) sb.append(' ');
+                    sb.append((providedInternalInsets[i]));
+                }
             }
-            if (!providedInternalImeInsets.equals(Insets.NONE)) {
+            if (providedInternalImeInsets != null) {
+                sb.append(System.lineSeparator());
                 sb.append(" providedInternalImeInsets=");
-                sb.append(providedInternalImeInsets);
+                for (int i = 0; i < providedInternalImeInsets.length; ++i) {
+                    if (i > 0) sb.append(' ');
+                    sb.append((providedInternalImeInsets[i]));
+                }
             }
             if (insetsRoundedCornerFrame) {
                 sb.append(" insetsRoundedCornerFrame=");
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 0008aa6..90e3498 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -5682,6 +5682,7 @@
          * @param heading Whether the item is a heading. (Prefer
          *                {@link AccessibilityNodeInfo#setHeading(boolean)})
          * @param selected Whether the item is selected.
+         * @removed
          */
         @Deprecated
         @NonNull
diff --git a/core/java/android/view/accessibility/IAccessibilityEmbeddedConnection.aidl b/core/java/android/view/accessibility/IAccessibilityEmbeddedConnection.aidl
index 707099e..75d81ed 100644
--- a/core/java/android/view/accessibility/IAccessibilityEmbeddedConnection.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityEmbeddedConnection.aidl
@@ -28,5 +28,5 @@
 
     void disassociateEmbeddedHierarchy();
 
-    oneway void setScreenMatrix(in float[] matrixValues);
+    oneway void setWindowMatrix(in float[] matrixValues);
 }
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
index deb0d2a..472a363 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -34,23 +34,24 @@
     void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, in Region bounds,
         int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
         int interrogatingPid, long interrogatingTid, in MagnificationSpec spec,
-        in Bundle arguments);
+        in float[] matrixValues, in Bundle arguments);
 
     void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, String viewId,
         in Region bounds, int interactionId, IAccessibilityInteractionConnectionCallback callback,
-        int flags, int interrogatingPid, long interrogatingTid, in MagnificationSpec spec);
+        int flags, int interrogatingPid, long interrogatingTid, in MagnificationSpec spec,
+         in float[] matrix);
 
     void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, in Region bounds,
         int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
-        int interrogatingPid, long interrogatingTid, in MagnificationSpec spec);
+        int interrogatingPid, long interrogatingTid, in MagnificationSpec spec, in float[] matrixValues);
 
     void findFocus(long accessibilityNodeId, int focusType, in Region bounds, int interactionId,
         IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
-        long interrogatingTid, in MagnificationSpec spec);
+        long interrogatingTid, in MagnificationSpec spec, in float[] matrixValues);
 
     void focusSearch(long accessibilityNodeId, int direction, in Region bounds, int interactionId,
         IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
-        long interrogatingTid, in MagnificationSpec spec);
+        long interrogatingTid, in MagnificationSpec spec, in float[] matrixValues);
 
     void performAccessibilityAction(long accessibilityNodeId, int action, in Bundle arguments,
         int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
diff --git a/core/java/android/window/TaskFragmentInfo.java b/core/java/android/window/TaskFragmentInfo.java
index a118f9a..f72164e 100644
--- a/core/java/android/window/TaskFragmentInfo.java
+++ b/core/java/android/window/TaskFragmentInfo.java
@@ -52,9 +52,6 @@
     @NonNull
     private final Configuration mConfiguration = new Configuration();
 
-    /** Whether the TaskFragment contains any child Window Container. */
-    private final boolean mIsEmpty;
-
     /** The number of the running activities in the TaskFragment. */
     private final int mRunningActivityCount;
 
@@ -77,21 +74,27 @@
      */
     private final boolean mIsTaskClearedForReuse;
 
+    /**
+     * Whether the last running activity in the TaskFragment was reparented to a different Task
+     * because it is entering PiP.
+     */
+    private final boolean mIsTaskFragmentClearedForPip;
+
     /** @hide */
     public TaskFragmentInfo(
             @NonNull IBinder fragmentToken, @NonNull WindowContainerToken token,
-            @NonNull Configuration configuration, boolean isEmpty, int runningActivityCount,
+            @NonNull Configuration configuration, int runningActivityCount,
             boolean isVisible, @NonNull List<IBinder> activities, @NonNull Point positionInParent,
-            boolean isTaskClearedForReuse) {
+            boolean isTaskClearedForReuse, boolean isTaskFragmentClearedForPip) {
         mFragmentToken = requireNonNull(fragmentToken);
         mToken = requireNonNull(token);
         mConfiguration.setTo(configuration);
-        mIsEmpty = isEmpty;
         mRunningActivityCount = runningActivityCount;
         mIsVisible = isVisible;
         mActivities.addAll(activities);
         mPositionInParent = requireNonNull(positionInParent);
         mIsTaskClearedForReuse = isTaskClearedForReuse;
+        mIsTaskFragmentClearedForPip = isTaskFragmentClearedForPip;
     }
 
     @NonNull
@@ -110,7 +113,7 @@
     }
 
     public boolean isEmpty() {
-        return mIsEmpty;
+        return mRunningActivityCount == 0;
     }
 
     public boolean hasRunningActivity() {
@@ -140,6 +143,11 @@
         return mIsTaskClearedForReuse;
     }
 
+    /** @hide */
+    public boolean isTaskFragmentClearedForPip() {
+        return mIsTaskFragmentClearedForPip;
+    }
+
     @WindowingMode
     public int getWindowingMode() {
         return mConfiguration.windowConfiguration.getWindowingMode();
@@ -156,25 +164,25 @@
 
         return mFragmentToken.equals(that.mFragmentToken)
                 && mToken.equals(that.mToken)
-                && mIsEmpty == that.mIsEmpty
                 && mRunningActivityCount == that.mRunningActivityCount
                 && mIsVisible == that.mIsVisible
                 && getWindowingMode() == that.getWindowingMode()
                 && mActivities.equals(that.mActivities)
                 && mPositionInParent.equals(that.mPositionInParent)
-                && mIsTaskClearedForReuse == that.mIsTaskClearedForReuse;
+                && mIsTaskClearedForReuse == that.mIsTaskClearedForReuse
+                && mIsTaskFragmentClearedForPip == that.mIsTaskFragmentClearedForPip;
     }
 
     private TaskFragmentInfo(Parcel in) {
         mFragmentToken = in.readStrongBinder();
         mToken = in.readTypedObject(WindowContainerToken.CREATOR);
         mConfiguration.readFromParcel(in);
-        mIsEmpty = in.readBoolean();
         mRunningActivityCount = in.readInt();
         mIsVisible = in.readBoolean();
         in.readBinderList(mActivities);
         mPositionInParent = requireNonNull(in.readTypedObject(Point.CREATOR));
         mIsTaskClearedForReuse = in.readBoolean();
+        mIsTaskFragmentClearedForPip = in.readBoolean();
     }
 
     /** @hide */
@@ -183,12 +191,12 @@
         dest.writeStrongBinder(mFragmentToken);
         dest.writeTypedObject(mToken, flags);
         mConfiguration.writeToParcel(dest, flags);
-        dest.writeBoolean(mIsEmpty);
         dest.writeInt(mRunningActivityCount);
         dest.writeBoolean(mIsVisible);
         dest.writeBinderList(mActivities);
         dest.writeTypedObject(mPositionInParent, flags);
         dest.writeBoolean(mIsTaskClearedForReuse);
+        dest.writeBoolean(mIsTaskFragmentClearedForPip);
     }
 
     @NonNull
@@ -210,12 +218,12 @@
         return "TaskFragmentInfo{"
                 + " fragmentToken=" + mFragmentToken
                 + " token=" + mToken
-                + " isEmpty=" + mIsEmpty
                 + " runningActivityCount=" + mRunningActivityCount
                 + " isVisible=" + mIsVisible
                 + " activities=" + mActivities
                 + " positionInParent=" + mPositionInParent
                 + " isTaskClearedForReuse=" + mIsTaskClearedForReuse
+                + " isTaskFragmentClearedForPip" + mIsTaskFragmentClearedForPip
                 + "}";
     }
 
diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java
index 9fe4d67..314b0a0 100644
--- a/core/java/com/android/internal/app/LocalePickerWithRegion.java
+++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java
@@ -129,6 +129,12 @@
         boolean isForCountryMode = parent != null;
 
         if (!TextUtils.isEmpty(appPackageName) && !isForCountryMode) {
+            // Filter current system locale to add them into suggestion
+            LocaleList systemLangList = LocaleList.getDefault();
+            for(int i = 0; i < systemLangList.size(); i++) {
+                langTagsToIgnore.add(systemLangList.get(i).toLanguageTag());
+            }
+
             if (appCurrentLocale != null) {
                 Log.d(TAG, "appCurrentLocale: " + appCurrentLocale.getLocale().toLanguageTag());
                 langTagsToIgnore.add(appCurrentLocale.getLocale().toLanguageTag());
@@ -168,9 +174,21 @@
                     result.mLocaleStatus == LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG
                     || result.mLocaleStatus == LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_ASSET;
 
+            // Add current system language into suggestion list
+            for(LocaleStore.LocaleInfo localeInfo: LocaleStore.getSystemCurrentLocaleInfo()) {
+                boolean isNotCurrentLocale = appCurrentLocale == null
+                        || !localeInfo.getLocale().equals(appCurrentLocale.getLocale());
+                if (!isForCountryMode && isNotCurrentLocale) {
+                    mLocaleList.add(localeInfo);
+                }
+            }
+
+            // Filter the language not support in app
             mLocaleList = filterTheLanguagesNotSupportedInApp(
                     shouldShowList, result.mAppSupportedLocales);
 
+            Log.d(TAG, "mLocaleList after app-supported filter:  " + mLocaleList.size());
+
             // Add "system language"
             if (!isForCountryMode && shouldShowList) {
                 mLocaleList.add(LocaleStore.getSystemDefaultLocaleInfo(appCurrentLocale == null));
@@ -190,7 +208,6 @@
                     }
                 }
             }
-            Log.d(TAG, "mLocaleList after app-supported filter:  " + filteredList.size());
         }
 
         return filteredList;
diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java
index eb11b9b..9480362 100644
--- a/core/java/com/android/internal/app/LocaleStore.java
+++ b/core/java/com/android/internal/app/LocaleStore.java
@@ -25,9 +25,11 @@
 import android.util.Log;
 
 import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.IllformedLocaleException;
+import java.util.List;
 import java.util.Locale;
 import java.util.Set;
 
@@ -278,6 +280,22 @@
     }
 
     /**
+     * Returns a list of system languages with LocaleInfo
+     */
+    public static List<LocaleInfo> getSystemCurrentLocaleInfo() {
+        List<LocaleInfo> localeList = new ArrayList<>();
+
+        LocaleList systemLangList = LocaleList.getDefault();
+        for(int i = 0; i < systemLangList.size(); i++) {
+            LocaleInfo systemLocaleInfo = new LocaleInfo(systemLangList.get(i));
+            systemLocaleInfo.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_SIM;
+            systemLocaleInfo.mIsTranslated = true;
+            localeList.add(systemLocaleInfo);
+        }
+        return localeList;
+    }
+
+    /**
      * The "system default" is special case for per-app picker. Intentionally keep the locale
      * empty to let activity know "system default" been selected.
      */
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index bfd8ff9..17c2dc0 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1480,7 +1480,7 @@
             if (intent != null) {
                 prepareIntentForCrossProfileLaunch(intent);
             }
-            safelyStartActivityInternal(otherProfileResolveInfo,
+            safelyStartActivityAsUser(otherProfileResolveInfo,
                     mMultiProfilePagerAdapter.getInactiveListAdapter().mResolverListController
                             .getUserHandle());
         });
diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
index 68b8968..18fde47 100644
--- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
+++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
@@ -217,22 +217,18 @@
                 break;
             case TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER:
                 if (!(convertView instanceof ViewGroup)) {
+                    TextView title;
                     if (((LocaleStore.LocaleInfo)getItem(position)).isAppCurrentLocale()) {
                         convertView = mInflater.inflate(
-                                R.layout.app_language_picker_system_current, parent, false);
+                                R.layout.app_language_picker_current_locale_item, parent, false);
+                        title = convertView.findViewById(R.id.language_picker_item);
                     } else {
                         convertView = mInflater.inflate(
-                                R.layout.app_language_picker_system_default, parent, false);
+                                R.layout.language_picker_item, parent, false);
+                        title = convertView.findViewById(R.id.locale);
                     }
+                    title.setText(R.string.system_locale_title);
                 }
-
-                Locale defaultLocale = Locale.getDefault();
-                TextView title = convertView.findViewById(R.id.locale);
-                title.setText(R.string.system_locale_title);
-                title.setTextLocale(defaultLocale);
-                TextView subtitle = convertView.findViewById(R.id.system_locale_subtitle);
-                subtitle.setText(defaultLocale.getDisplayName());
-                subtitle.setTextLocale(defaultLocale);
                 break;
             case TYPE_CURRENT_LOCALE:
                 if (!(convertView instanceof ViewGroup)) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 2f7c015..1db4bbb 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -22,7 +22,7 @@
 import static android.os.Build.VERSION_CODES.N;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.InsetsState.clearCompatInsets;
+import static android.view.InsetsState.clearsCompatInsets;
 import static android.view.View.MeasureSpec.AT_MOST;
 import static android.view.View.MeasureSpec.EXACTLY;
 import static android.view.View.MeasureSpec.getMode;
@@ -1120,11 +1120,11 @@
                     : controller.getSystemBarsAppearance();
 
             if (insets != null) {
-                final boolean clearCompatInsets = clearCompatInsets(attrs.type, attrs.flags,
+                final boolean clearsCompatInsets = clearsCompatInsets(attrs.type, attrs.flags,
                         getResources().getConfiguration().windowConfiguration.getWindowingMode());
                 final Insets stableBarInsets = insets.getInsetsIgnoringVisibility(
                         WindowInsets.Type.systemBars());
-                final Insets systemInsets = clearCompatInsets
+                final Insets systemInsets = clearsCompatInsets
                         ? Insets.NONE
                         : Insets.min(insets.getInsets(WindowInsets.Type.systemBars()
                                 | WindowInsets.Type.displayCutout()), stableBarInsets);
diff --git a/core/java/com/android/internal/security/OWNERS b/core/java/com/android/internal/security/OWNERS
index 41d1d66..b702df8 100644
--- a/core/java/com/android/internal/security/OWNERS
+++ b/core/java/com/android/internal/security/OWNERS
@@ -1,3 +1,3 @@
 # Bug component: 36824
 
-per-file VerityUtils.java = victorhsieh@google.com
+per-file VerityUtils.java = file:platform/system/security:/fsverity/OWNERS
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 9e5f6ea..ac6b80f 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -84,7 +84,7 @@
 per-file LayoutlibLoader.cpp = diegoperez@google.com, jgaillard@google.com
 
 # Verity
-per-file com_android_internal_security_Verity* = ebiggers@google.com, victorhsieh@google.com
+per-file com_android_internal_security_Verity* = file:platform/system/security:/fsverity/OWNERS
 
 # VINTF
 per-file android_os_VintfObject* = file:platform/system/libvintf:/OWNERS
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index bce4ed7..8012e0c 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -33,12 +33,13 @@
 
 #include <nativehelper/ScopedUtfChars.h>
 
+#include "android_media_AudioAttributes.h"
 #include "android_media_AudioFormat.h"
 #include "android_media_AudioErrors.h"
 #include "android_media_DeviceCallback.h"
+#include "android_media_JNIUtils.h"
 #include "android_media_MediaMetricsJNI.h"
 #include "android_media_MicrophoneInfo.h"
-#include "android_media_AudioAttributes.h"
 
 
 // ----------------------------------------------------------------------------
@@ -57,8 +58,7 @@
     // these fields provide access from C++ to the...
     jmethodID postNativeEventInJava; //... event post callback method
     jfieldID  nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
-    jfieldID  nativeCallbackCookie;    // provides access to the AudioRecord callback data
-    jfieldID  nativeDeviceCallback;    // provides access to the JNIDeviceCallback instance
+    jfieldID  jniData;    // provides access to AudioRecord JNI Handle
 };
 static audio_record_fields_t     javaAudioRecordFields;
 static struct {
@@ -66,15 +66,68 @@
     jfieldID  fieldNanoTime;          // AudioTimestamp.nanoTime
 } javaAudioTimestampFields;
 
-struct audiorecord_callback_cookie {
-    jclass      audioRecord_class;
-    jobject     audioRecord_ref;
-    bool        busy;
-    Condition   cond;
-};
 
-static Mutex sLock;
-static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies;
+class AudioRecordJNIStorage : public AudioRecord::IAudioRecordCallback {
+ private:
+   // Keep in sync with frameworks/base/media/java/android/media/AudioRecord.java NATIVE_EVENT_*.
+   enum class EventType {
+        EVENT_MORE_DATA = 0,        // Request to read available data from buffer.
+                                    // If this event is delivered but the callback handler
+                                    // does not want to read the available data, the handler must
+                                    // explicitly ignore the event by setting frameCount to zero.
+        EVENT_OVERRUN = 1,          // Buffer overrun occurred.
+        EVENT_MARKER = 2,           // Record head is at the specified marker position
+                                    // (See setMarkerPosition()).
+        EVENT_NEW_POS = 3,          // Record head is at a new position
+                                    // (See setPositionUpdatePeriod()).
+        EVENT_NEW_IAUDIORECORD = 4, // IAudioRecord was re-created, either due to re-routing and
+                                    // voluntary invalidation by mediaserver, or mediaserver crash.
+    };
+
+  public:
+    AudioRecordJNIStorage(jclass audioRecordClass, jobject audioRecordWeakRef)
+          : mAudioRecordClass(audioRecordClass), mAudioRecordWeakRef(audioRecordWeakRef) {}
+    AudioRecordJNIStorage(const AudioRecordJNIStorage &) = delete;
+    AudioRecordJNIStorage& operator=(const AudioRecordJNIStorage &) = delete;
+
+    void onMarker(uint32_t) override {
+        postEvent(EventType::EVENT_MARKER);
+    }
+
+    void onNewPos(uint32_t) override {
+        postEvent(EventType::EVENT_NEW_POS);
+    }
+
+    void setDeviceCallback(const sp<JNIDeviceCallback>& callback) {
+        mDeviceCallback = callback;
+    }
+
+    sp<JNIDeviceCallback> getDeviceCallback() const { return mDeviceCallback; }
+
+    jobject getAudioTrackWeakRef() const & { return mAudioRecordWeakRef.get(); }
+
+    // If we attempt to get a jobject from a rvalue, it will soon go out of
+    // scope, and the reference count can drop to zero, which is unsafe.
+    jobject getAudioTrackWeakRef() const && = delete;
+
+  private:
+    void postEvent(EventType event, int arg = 0) const {
+      JNIEnv *env = getJNIEnvOrDie();
+      env->CallStaticVoidMethod(
+          static_cast<jclass>(mAudioRecordClass.get()),
+          javaAudioRecordFields.postNativeEventInJava,
+          mAudioRecordWeakRef.get(), static_cast<int>(event), arg, 0, nullptr);
+      if (env->ExceptionCheck()) {
+          env->ExceptionDescribe();
+          env->ExceptionClear();
+      }
+    }
+
+    // Mutation of this object is protected using Java concurrency constructs
+    sp<JNIDeviceCallback> mDeviceCallback;
+    const GlobalRef   mAudioRecordClass;
+    const GlobalRef   mAudioRecordWeakRef;
+};
 
 // ----------------------------------------------------------------------------
 
@@ -85,103 +138,9 @@
 #define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED    (-20)
 
 // ----------------------------------------------------------------------------
-static void recorderCallback(int event, void* user, void *info) {
-
-    audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
-    {
-        Mutex::Autolock l(sLock);
-        if (sAudioRecordCallBackCookies.indexOf(callbackInfo) < 0) {
-            return;
-        }
-        callbackInfo->busy = true;
-    }
-
-    switch (event) {
-    case AudioRecord::EVENT_MARKER: {
-        JNIEnv *env = AndroidRuntime::getJNIEnv();
-        if (user != NULL && env != NULL) {
-            env->CallStaticVoidMethod(
-                callbackInfo->audioRecord_class,
-                javaAudioRecordFields.postNativeEventInJava,
-                callbackInfo->audioRecord_ref, event, 0,0, NULL);
-            if (env->ExceptionCheck()) {
-                env->ExceptionDescribe();
-                env->ExceptionClear();
-            }
-        }
-        } break;
-
-    case AudioRecord::EVENT_NEW_POS: {
-        JNIEnv *env = AndroidRuntime::getJNIEnv();
-        if (user != NULL && env != NULL) {
-            env->CallStaticVoidMethod(
-                callbackInfo->audioRecord_class,
-                javaAudioRecordFields.postNativeEventInJava,
-                callbackInfo->audioRecord_ref, event, 0,0, NULL);
-            if (env->ExceptionCheck()) {
-                env->ExceptionDescribe();
-                env->ExceptionClear();
-            }
-        }
-        } break;
-    }
-
-    {
-        Mutex::Autolock l(sLock);
-        callbackInfo->busy = false;
-        callbackInfo->cond.broadcast();
-    }
-}
-
-static sp<JNIDeviceCallback> getJniDeviceCallback(JNIEnv* env, jobject thiz)
-{
-    Mutex::Autolock l(sLock);
-    JNIDeviceCallback* const cb =
-            (JNIDeviceCallback*)env->GetLongField(thiz,
-                                                  javaAudioRecordFields.nativeDeviceCallback);
-    return sp<JNIDeviceCallback>(cb);
-}
-
-static sp<JNIDeviceCallback> setJniDeviceCallback(JNIEnv* env,
-                                                  jobject thiz,
-                                                  const sp<JNIDeviceCallback>& cb)
-{
-    Mutex::Autolock l(sLock);
-    sp<JNIDeviceCallback> old =
-            (JNIDeviceCallback*)env->GetLongField(thiz,
-                                                  javaAudioRecordFields.nativeDeviceCallback);
-    if (cb.get()) {
-        cb->incStrong((void*)setJniDeviceCallback);
-    }
-    if (old != 0) {
-        old->decStrong((void*)setJniDeviceCallback);
-    }
-    env->SetLongField(thiz, javaAudioRecordFields.nativeDeviceCallback, (jlong)cb.get());
-    return old;
-}
-
-// ----------------------------------------------------------------------------
 static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
 {
-    Mutex::Autolock l(sLock);
-    AudioRecord* const ar =
-            (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
-    return sp<AudioRecord>(ar);
-}
-
-static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar)
-{
-    Mutex::Autolock l(sLock);
-    sp<AudioRecord> old =
-            (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
-    if (ar.get()) {
-        ar->incStrong((void*)setAudioRecord);
-    }
-    if (old != 0) {
-        old->decStrong((void*)setAudioRecord);
-    }
-    env->SetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (jlong)ar.get());
-    return old;
+    return getFieldSp<AudioRecord>(env, thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
 }
 
 // ----------------------------------------------------------------------------
@@ -211,9 +170,8 @@
     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
     nSession = NULL;
 
-    sp<AudioRecord> lpRecorder = 0;
-    audiorecord_callback_cookie *lpCallbackData = NULL;
-
+    sp<AudioRecord> lpRecorder;
+    sp<AudioRecordJNIStorage> callbackData;
     jclass clazz = env->GetObjectClass(thiz);
     if (clazz == NULL) {
         ALOGE("Can't find %s when setting up callback.", kClassPathName);
@@ -287,18 +245,14 @@
         }
         // create the callback information:
         // this data will be passed with every AudioRecord callback
-        lpCallbackData = new audiorecord_callback_cookie;
-        lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
         // we use a weak reference so the AudioRecord object can be garbage collected.
-        lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
-        lpCallbackData->busy = false;
+        callbackData = sp<AudioRecordJNIStorage>::make(clazz, weak_this);
 
         const status_t status =
                 lpRecorder->set(paa->source, sampleRateInHertz,
                                 format, // word length, PCM
                                 localChanMask, frameCount,
-                                recorderCallback, // callback_t
-                                lpCallbackData,   // void* user
+                                callbackData,   // callback
                                 0,                // notificationFrames,
                                 true,             // threadCanCallJava
                                 sessionId, AudioRecord::TRANSFER_DEFAULT, flags, -1,
@@ -330,11 +284,8 @@
 
         // create the callback information:
         // this data will be passed with every AudioRecord callback
-        lpCallbackData = new audiorecord_callback_cookie;
-        lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
-        // we use a weak reference so the AudioRecord object can be garbage collected.
-        lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
-        lpCallbackData->busy = false;
+        // This next line makes little sense
+        // callbackData = sp<AudioRecordJNIStorage>::make(clazz, weak_this);
     }
 
     nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
@@ -352,26 +303,20 @@
         env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
     }
 
-    {   // scope for the lock
-        Mutex::Autolock l(sLock);
-        sAudioRecordCallBackCookies.add(lpCallbackData);
-    }
     // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
     // of the Java object
-    setAudioRecord(env, thiz, lpRecorder);
+    setFieldSp(env, thiz, lpRecorder, javaAudioRecordFields.nativeRecorderInJavaObj);
 
-    // save our newly created callback information in the "nativeCallbackCookie" field
-    // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
-    env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData);
+    // save our newly created callback information in the "jniData" field
+    // of the Java object (in mNativeJNIDataHandle) so we can free the memory in finalize()
+    setFieldSp(env, thiz, callbackData, javaAudioRecordFields.jniData);
 
     return (jint) AUDIO_JAVA_SUCCESS;
 
     // failure:
 native_init_failure:
-    env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
-    env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
-    delete lpCallbackData;
-    env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
+    setFieldSp(env, thiz, sp<AudioRecord>{}, javaAudioRecordFields.nativeRecorderInJavaObj);
+    setFieldSp(env, thiz, sp<AudioRecordJNIStorage>{}, javaAudioRecordFields.jniData);
 
     // lpRecorder goes out of scope, so reference count drops to zero
     return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
@@ -411,36 +356,9 @@
 
 #define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
 static void android_media_AudioRecord_release(JNIEnv *env,  jobject thiz) {
-    sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
-    if (lpRecorder == NULL) {
-        return;
-    }
-    ALOGV("About to delete lpRecorder: %p", lpRecorder.get());
-    lpRecorder->stop();
 
-    audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetLongField(
-        thiz, javaAudioRecordFields.nativeCallbackCookie);
-
-    // reset the native resources in the Java object so any attempt to access
-    // them after a call to release fails.
-    env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
-
-    // delete the callback information
-    if (lpCookie) {
-        Mutex::Autolock l(sLock);
-        ALOGV("deleting lpCookie: %p", lpCookie);
-        while (lpCookie->busy) {
-            if (lpCookie->cond.waitRelative(sLock,
-                                            milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
-                                                    NO_ERROR) {
-                break;
-            }
-        }
-        sAudioRecordCallBackCookies.remove(lpCookie);
-        env->DeleteGlobalRef(lpCookie->audioRecord_class);
-        env->DeleteGlobalRef(lpCookie->audioRecord_ref);
-        delete lpCookie;
-    }
+    setFieldSp(env, thiz, sp<AudioRecord>{}, javaAudioRecordFields.nativeRecorderInJavaObj);
+    setFieldSp(env, thiz, sp<AudioRecordJNIStorage>{}, javaAudioRecordFields.jniData);
 }
 
 
@@ -685,43 +603,40 @@
     return (jint)lpRecorder->getRoutedDeviceId();
 }
 
+// Enable and Disable Callback methods are synchronized on the Java side
 static void android_media_AudioRecord_enableDeviceCallback(
                 JNIEnv *env,  jobject thiz) {
 
     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
-    if (lpRecorder == 0) {
+    if (lpRecorder == nullptr) {
         return;
     }
-    sp<JNIDeviceCallback> cb = getJniDeviceCallback(env, thiz);
-    if (cb != 0) {
-        return;
-    }
-    audiorecord_callback_cookie *cookie =
-            (audiorecord_callback_cookie *)env->GetLongField(thiz,
-                                                     javaAudioRecordFields.nativeCallbackCookie);
-    if (cookie == NULL) {
+    const auto pJniStorage =
+            getFieldSp<AudioRecordJNIStorage>(env, thiz, javaAudioRecordFields.jniData);
+   if (pJniStorage == nullptr || pJniStorage->getDeviceCallback() != nullptr) {
         return;
     }
 
-    cb = new JNIDeviceCallback(env, thiz, cookie->audioRecord_ref,
-                               javaAudioRecordFields.postNativeEventInJava);
-    status_t status = lpRecorder->addAudioDeviceCallback(cb);
-    if (status == NO_ERROR) {
-        setJniDeviceCallback(env, thiz, cb);
-    }
+    pJniStorage->setDeviceCallback(
+            sp<JNIDeviceCallback>::make(env, thiz, pJniStorage->getAudioTrackWeakRef(),
+                                        javaAudioRecordFields.postNativeEventInJava));
+    lpRecorder->addAudioDeviceCallback(pJniStorage->getDeviceCallback());
 }
 
 static void android_media_AudioRecord_disableDeviceCallback(
                 JNIEnv *env,  jobject thiz) {
-
     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
-    if (lpRecorder == 0) {
+    if (lpRecorder == nullptr) {
         return;
     }
-    sp<JNIDeviceCallback> cb = setJniDeviceCallback(env, thiz, 0);
-    if (cb != 0) {
-        lpRecorder->removeAudioDeviceCallback(cb);
+    const auto pJniStorage =
+            getFieldSp<AudioRecordJNIStorage>(env, thiz, javaAudioRecordFields.jniData);
+
+    if (pJniStorage == nullptr || pJniStorage->getDeviceCallback() == nullptr) {
+        return;
     }
+    lpRecorder->removeAudioDeviceCallback(pJniStorage->getDeviceCallback());
+    pJniStorage->setDeviceCallback(nullptr);
 }
 
 // ----------------------------------------------------------------------------
@@ -962,17 +877,15 @@
 
 // field names found in android/media/AudioRecord.java
 #define JAVA_POSTEVENT_CALLBACK_NAME  "postEventFromNative"
-#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME  "mNativeRecorderInJavaObj"
-#define JAVA_NATIVECALLBACKINFO_FIELD_NAME       "mNativeCallbackCookie"
-#define JAVA_NATIVEDEVICECALLBACK_FIELD_NAME       "mNativeDeviceCallback"
+#define JAVA_NATIVEAUDIORECORDERHANDLE_FIELD_NAME  "mNativeAudioRecordHandle"
+#define JAVA_NATIVEJNIDATAHANDLE_FIELD_NAME        "mNativeJNIDataHandle"
 
 // ----------------------------------------------------------------------------
 int register_android_media_AudioRecord(JNIEnv *env)
 {
     javaAudioRecordFields.postNativeEventInJava = NULL;
     javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
-    javaAudioRecordFields.nativeCallbackCookie = NULL;
-    javaAudioRecordFields.nativeDeviceCallback = NULL;
+    javaAudioRecordFields.jniData = NULL;
 
 
     // Get the AudioRecord class
@@ -983,15 +896,12 @@
             "(Ljava/lang/Object;IIILjava/lang/Object;)V");
 
     // Get the variables
-    //    mNativeRecorderInJavaObj
+    //    mNativeAudioRecordHandle
     javaAudioRecordFields.nativeRecorderInJavaObj = GetFieldIDOrDie(env,
-            audioRecordClass, JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "J");
-    //     mNativeCallbackCookie
-    javaAudioRecordFields.nativeCallbackCookie = GetFieldIDOrDie(env,
-            audioRecordClass, JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J");
-
-    javaAudioRecordFields.nativeDeviceCallback = GetFieldIDOrDie(env,
-            audioRecordClass, JAVA_NATIVEDEVICECALLBACK_FIELD_NAME, "J");
+            audioRecordClass, JAVA_NATIVEAUDIORECORDERHANDLE_FIELD_NAME, "J");
+    //   mNativeJNIDataHandle
+    javaAudioRecordFields.jniData = GetFieldIDOrDie(env,
+            audioRecordClass, JAVA_NATIVEJNIDATAHANDLE_FIELD_NAME, "J");
 
     // Get the RecordTimestamp class and fields
     jclass audioTimestampClass = FindClassOrDie(env, "android/media/AudioTimestamp");
diff --git a/core/jni/android_media_JNIUtils.h b/core/jni/android_media_JNIUtils.h
index 9da5fa3..a413d09 100644
--- a/core/jni/android_media_JNIUtils.h
+++ b/core/jni/android_media_JNIUtils.h
@@ -64,5 +64,39 @@
     return env;
 }
 
+class GlobalRef {
+  public:
+    GlobalRef(jobject object) : GlobalRef(object, AndroidRuntime::getJNIEnv()) {}
+
+    GlobalRef(jobject object, JNIEnv* env) {
+        LOG_ALWAYS_FATAL_IF(env == nullptr, "Invalid JNIEnv when attempting to create a GlobalRef");
+        mGlobalRef = env->NewGlobalRef(object);
+        LOG_ALWAYS_FATAL_IF(env->IsSameObject(object, nullptr) == JNI_TRUE,
+                            "Creating GlobalRef from null object");
+    }
+
+    GlobalRef(const GlobalRef& other) : GlobalRef(other.mGlobalRef) {}
+
+    GlobalRef(GlobalRef&& other) : mGlobalRef(other.mGlobalRef) { other.mGlobalRef = nullptr; }
+
+    // Logically const
+    GlobalRef& operator=(const GlobalRef& other) = delete;
+
+    GlobalRef& operator=(GlobalRef&& other) = delete;
+
+    ~GlobalRef() {
+        if (mGlobalRef == nullptr) return; // No reference to decrement
+        getJNIEnvOrDie()->DeleteGlobalRef(mGlobalRef);
+    }
+
+    // Valid as long as this wrapper is in scope.
+    jobject get() const {
+      return mGlobalRef;
+    }
+
+  private:
+    // Logically const. Not actually const so we can move from GlobalRef
+    jobject mGlobalRef;
+};
 
 } // namespace android
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index a6fbf094..b9d5ee4 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -913,7 +913,7 @@
             end = i;
             i++;
         } else if ((mode&PROC_QUOTES) != 0) {
-            while (buffer[i] != '"' && i < endIndex) {
+            while (i < endIndex && buffer[i] != '"') {
                 i++;
             }
             end = i;
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 518fc09..51a708b 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -24,7 +24,9 @@
 
 #include <memory>
 
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
 #include <android-base/chrono_utils.h>
+#include <android/graphics/properties.h>
 #include <android/graphics/region.h>
 #include <android/gui/BnScreenCaptureListener.h>
 #include <android/hardware/display/IDeviceProductInfoConstants.h>
@@ -1888,6 +1890,11 @@
         return nullptr;
     }
 
+    using aidl::android::hardware::graphics::common::PixelFormat;
+    if (support.value().format == PixelFormat::R_8 && !hwui_uses_vulkan()) {
+        return nullptr;
+    }
+
     jobject jDisplayDecorationSupport =
             env->NewObject(gDisplayDecorationSupportInfo.clazz, gDisplayDecorationSupportInfo.ctor);
     if (jDisplayDecorationSupport == nullptr) {
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index cd3ba1e..0e3840a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5678,8 +5678,8 @@
          restricted level.
     -->
     <array name="config_bg_current_drain_threshold_to_bg_restricted">
-        <item>4.0</item> <!-- regular device -->
-        <item>8.0</item> <!-- low ram device -->
+        <item>10.0</item> <!-- regular device -->
+        <item>20.0</item> <!-- low ram device -->
     </array>
 
     <!-- The background current drain monitoring window size. -->
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index cd3578c..77500c4 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -17,9 +17,9 @@
 <resources>
     <!-- This file defines Android telephony related resources -->
 
-    <!-- Whether force to enable telephony new data stack or not -->
-    <bool name="config_force_enable_telephony_new_data_stack">true</bool>
-    <java-symbol type="bool" name="config_force_enable_telephony_new_data_stack" />
+    <!-- Whether force disabling telephony new data stack or not -->
+    <bool name="config_force_disable_telephony_new_data_stack">false</bool>
+    <java-symbol type="bool" name="config_force_disable_telephony_new_data_stack" />
 
     <!-- Configure tcp buffer sizes per network type in the form:
          network-type:rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index a476189..d9d1a08 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -782,6 +782,10 @@
     <dimen name="notification_left_icon_start">@dimen/notification_icon_circle_start</dimen>
     <!-- The alpha of a disabled notification button -->
     <item type="dimen" format="float" name="notification_action_disabled_alpha">0.5</item>
+    <!-- The maximum size of Person avatar image in MessagingStyle notifications.
+         This is bigger than displayed because listeners can use it for other displays
+         e.g. wearables. -->
+    <dimen name="notification_person_icon_max_size">144dp</dimen>
 
     <!-- The maximum size of the small notification icon on low memory devices. -->
     <dimen name="notification_small_icon_size_low_ram">@dimen/notification_small_icon_size</dimen>
@@ -795,6 +799,10 @@
     <dimen name="notification_big_picture_max_width_low_ram">294dp</dimen>
     <!-- The size of the right icon image when on low ram -->
     <dimen name="notification_right_icon_size_low_ram">@dimen/notification_right_icon_size</dimen>
+    <!-- The maximum size of Person avatar image in MessagingStyle notifications.
+     This is bigger than displayed because listeners can use it for other displays
+     e.g. wearables. -->
+    <dimen name="notification_person_icon_max_size_low_ram">96dp</dimen>
     <!-- The maximum size of the grayscale icon -->
     <dimen name="notification_grayscale_icon_max_size">256dp</dimen>
 
diff --git a/core/res/res/values/public-final.xml b/core/res/res/values/public-final.xml
index 19a4842..85325fe 100644
--- a/core/res/res/values/public-final.xml
+++ b/core/res/res/values/public-final.xml
@@ -3222,4 +3222,184 @@
   <public type="id" name="accessibilityActionDragDrop" id="0x01020056" />
   <public type="id" name="accessibilityActionDragCancel" id="0x01020057" />
 
+  <!-- ===============================================================
+    Resources added in version T of the platform
+
+    NOTE: After this version of the platform is forked, changes cannot be made to the root
+    branch's groups for that release. Only merge changes to the forked platform branch.
+    =============================================================== -->
+  <eat-comment/>
+
+  <staging-public-group-final type="attr" first-id="0x01df0000">
+    <public name="sharedUserMaxSdkVersion" />
+    <public name="requiredSplitTypes" />
+    <public name="splitTypes" />
+    <public name="canDisplayOnRemoteDevices" />
+    <public name="supportedTypes" />
+    <public name="resetEnabledSettingsOnAppDataCleared" />
+    <public name="supportsStylusHandwriting" />
+    <public name="showClockAndComplications" />
+    <!-- @hide @SystemApi -->
+    <public name="gameSessionService" />
+    <public name="supportsBatteryGameMode" />
+    <public name="supportsPerformanceGameMode" />
+    <public name="allowGameAngleDriver" />
+    <public name="allowGameDownscaling" />
+    <public name="allowGameFpsOverride" />
+    <public name="localeConfig" />
+    <public name="showBackdrop" />
+    <public name="removed_useTargetActivityForQuickAccess"/>
+    <public name="removed_inheritKeyStoreKeys" />
+    <public name="preferKeepClear" />
+    <public name="autoHandwritingEnabled" />
+    <public name="fromExtendLeft" />
+    <public name="fromExtendTop" />
+    <public name="fromExtendRight" />
+    <public name="fromExtendBottom" />
+    <public name="toExtendLeft" />
+    <public name="toExtendTop" />
+    <public name="toExtendRight" />
+    <public name="toExtendBottom" />
+    <public name="tileService" />
+    <public name="windowSplashScreenBehavior" />
+    <public name="allowUntrustedActivityEmbedding" />
+    <public name="knownActivityEmbeddingCerts" />
+    <public name="intro" />
+    <public name="enableOnBackInvokedCallback" />
+    <public name="supportsInlineSuggestionsWithTouchExploration" />
+    <public name="lineBreakStyle" />
+    <public name="lineBreakWordStyle" />
+    <!-- @hide -->
+    <public name="maxDrawableWidth" />
+    <!-- @hide -->
+    <public name="maxDrawableHeight" />
+    <public name="backdropColor" />
+  </staging-public-group-final>
+
+  <public type="attr" name="sharedUserMaxSdkVersion" id="0x0101064d" />
+  <public type="attr" name="requiredSplitTypes" id="0x0101064e" />
+  <public type="attr" name="splitTypes" id="0x0101064f" />
+  <public type="attr" name="canDisplayOnRemoteDevices" id="0x01010650" />
+  <public type="attr" name="supportedTypes" id="0x01010651" />
+  <public type="attr" name="resetEnabledSettingsOnAppDataCleared" id="0x01010652" />
+  <public type="attr" name="supportsStylusHandwriting" id="0x01010653" />
+  <public type="attr" name="showClockAndComplications" id="0x01010654" />
+    <!-- @hide @SystemApi -->
+  <public type="attr" name="gameSessionService" id="0x01010655" />
+  <public type="attr" name="supportsBatteryGameMode" id="0x01010656" />
+  <public type="attr" name="supportsPerformanceGameMode" id="0x01010657" />
+  <public type="attr" name="allowGameAngleDriver" id="0x01010658" />
+  <public type="attr" name="allowGameDownscaling" id="0x01010659" />
+  <public type="attr" name="allowGameFpsOverride" id="0x0101065a" />
+  <public type="attr" name="localeConfig" id="0x0101065b" />
+  <public type="attr" name="showBackdrop" id="0x0101065c" />
+  <public type="attr" name="preferKeepClear" id="0x0101065d" />
+  <public type="attr" name="autoHandwritingEnabled" id="0x0101065e" />
+  <public type="attr" name="fromExtendLeft" id="0x0101065f" />
+  <public type="attr" name="fromExtendTop" id="0x01010660" />
+  <public type="attr" name="fromExtendRight" id="0x01010661" />
+  <public type="attr" name="fromExtendBottom" id="0x01010662" />
+  <public type="attr" name="toExtendLeft" id="0x01010663" />
+  <public type="attr" name="toExtendTop" id="0x01010664" />
+  <public type="attr" name="toExtendRight" id="0x01010665" />
+  <public type="attr" name="toExtendBottom" id="0x01010666" />
+  <public type="attr" name="tileService" id="0x01010667" />
+  <public type="attr" name="windowSplashScreenBehavior" id="0x01010668" />
+  <public type="attr" name="allowUntrustedActivityEmbedding" id="0x01010669" />
+  <public type="attr" name="knownActivityEmbeddingCerts" id="0x0101066a" />
+  <public type="attr" name="intro" id="0x0101066b" />
+  <public type="attr" name="enableOnBackInvokedCallback" id="0x0101066c" />
+  <public type="attr" name="supportsInlineSuggestionsWithTouchExploration" id="0x0101066d" />
+  <public type="attr" name="lineBreakStyle" id="0x0101066e" />
+  <public type="attr" name="lineBreakWordStyle" id="0x0101066f" />
+    <!-- @hide -->
+  <public type="attr" name="maxDrawableWidth" id="0x01010670" />
+    <!-- @hide -->
+  <public type="attr" name="maxDrawableHeight" id="0x01010671" />
+  <public type="attr" name="backdropColor" id="0x01010672" />
+
+  <staging-public-group-final type="id" first-id="0x01de0000">
+    <public name="removed_accessibilityActionSwipeLeft" />
+    <public name="removed_accessibilityActionSwipeRight" />
+    <public name="removed_accessibilityActionSwipeUp" />
+    <public name="removed_accessibilityActionSwipeDown" />
+    <public name="accessibilityActionShowTextSuggestions" />
+    <public name="inputExtractAction" />
+    <public name="inputExtractAccessories" />
+  </staging-public-group-final>
+
+  <public type="id" name="accessibilityActionShowTextSuggestions" id="0x01020058" />
+  <public type="id" name="inputExtractAction" id="0x01020059" />
+  <public type="id" name="inputExtractAccessories" id="0x0102005a" />
+
+  <staging-public-group-final type="style" first-id="0x01dd0000">
+    <public name="TextAppearance.DeviceDefault.Headline" />
+  </staging-public-group-final>
+
+  <public type="style" name="TextAppearance.DeviceDefault.Headline" id="0x010302e5" />
+
+  <staging-public-group-final type="string" first-id="0x01dc0000">
+    <!-- @hide @SystemApi -->
+    <public name="config_systemSupervision" />
+    <!-- @hide @SystemApi -->
+    <public name="config_devicePolicyManagement" />
+    <!-- @hide @SystemApi -->
+    <public name="config_systemAppProtectionService" />
+    <!-- @hide @SystemApi @TestApi -->
+    <public name="config_systemAutomotiveCalendarSyncManager" />
+    <!-- @hide @SystemApi -->
+    <public name="config_defaultAutomotiveNavigation" />
+    <!-- @hide @SystemApi -->
+    <public name="safety_protection_display_text" />
+    <!-- @hide @SystemApi -->
+    <public name="config_systemSettingsIntelligence" />
+    <!-- @hide -->
+    <public name="config_systemBluetoothStack" />
+  </staging-public-group-final>
+
+    <!-- @hide @SystemApi -->
+  <public type="string" name="config_systemSupervision" id="0x0104003c" />
+    <!-- @hide @SystemApi -->
+  <public type="string" name="config_devicePolicyManagement" id="0x0104003d" />
+    <!-- @hide @SystemApi -->
+  <public type="string" name="config_systemAppProtectionService" id="0x0104003e" />
+    <!-- @hide @SystemApi @TestApi -->
+  <public type="string" name="config_systemAutomotiveCalendarSyncManager" id="0x0104003f" />
+    <!-- @hide @SystemApi -->
+  <public type="string" name="config_defaultAutomotiveNavigation" id="0x01040040" />
+    <!-- @hide @SystemApi -->
+  <public type="string" name="safety_protection_display_text" id="0x01040041" />
+    <!-- @hide @SystemApi -->
+  <public type="string" name="config_systemSettingsIntelligence" id="0x01040042" />
+    <!-- @hide -->
+  <public type="string" name="config_systemBluetoothStack" id="0x01040043" />
+
+  <staging-public-group-final type="array" first-id="0x01d90000">
+    <!-- @hide @SystemApi -->
+    <public name="config_optionalIpSecAlgorithms" />
+  </staging-public-group-final>
+
+    <!-- @hide @SystemApi -->
+  <public type="array" name="config_optionalIpSecAlgorithms" id="0x01070006" />
+
+  <staging-public-group-final type="drawable" first-id="0x01d80000">
+    <!-- @hide @SystemApi -->
+    <public name="ic_safety_protection" />
+  </staging-public-group-final>
+
+    <!-- @hide @SystemApi -->
+  <public type="drawable" name="ic_safety_protection" id="0x010800b5" />
+
+  <staging-public-group-final type="bool" first-id="0x01cf0000">
+    <!-- @hide @TestApi -->
+    <public name="config_preventImeStartupUnlessTextEditor" />
+    <!-- @hide @SystemApi -->
+    <public name="config_enableQrCodeScannerOnLockScreen" />
+  </staging-public-group-final>
+
+    <!-- @hide @TestApi -->
+  <public type="bool" name="config_preventImeStartupUnlessTextEditor" id="0x01110007" />
+    <!-- @hide @SystemApi -->
+  <public type="bool" name="config_enableQrCodeScannerOnLockScreen" id="0x01110008" />
+
 </resources>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 86bad7f..f09ffbe 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -102,140 +102,65 @@
 <resources>
 
   <!-- ===============================================================
-    Resources added in version T of the platform
+    Resources added in version U of the platform
 
     NOTE: After this version of the platform is forked, changes cannot be made to the root
     branch's groups for that release. Only merge changes to the forked platform branch.
     =============================================================== -->
   <eat-comment/>
 
-  <staging-public-group type="attr" first-id="0x01df0000">
-    <public name="sharedUserMaxSdkVersion" />
-    <public name="requiredSplitTypes" />
-    <public name="splitTypes" />
-    <public name="canDisplayOnRemoteDevices" />
-    <public name="supportedTypes" />
-    <public name="resetEnabledSettingsOnAppDataCleared" />
-    <public name="supportsStylusHandwriting" />
-    <public name="showClockAndComplications" />
-    <!-- @hide @SystemApi -->
-    <public name="gameSessionService" />
-    <public name="supportsBatteryGameMode" />
-    <public name="supportsPerformanceGameMode" />
-    <public name="allowGameAngleDriver" />
-    <public name="allowGameDownscaling" />
-    <public name="allowGameFpsOverride" />
-    <public name="localeConfig" />
-    <public name="showBackdrop" />
-    <public name="removed_useTargetActivityForQuickAccess"/>
-    <public name="removed_inheritKeyStoreKeys" />
-    <public name="preferKeepClear" />
-    <public name="autoHandwritingEnabled" />
-    <public name="fromExtendLeft" />
-    <public name="fromExtendTop" />
-    <public name="fromExtendRight" />
-    <public name="fromExtendBottom" />
-    <public name="toExtendLeft" />
-    <public name="toExtendTop" />
-    <public name="toExtendRight" />
-    <public name="toExtendBottom" />
-    <public name="tileService" />
-    <public name="windowSplashScreenBehavior" />
-    <public name="allowUntrustedActivityEmbedding" />
-    <public name="knownActivityEmbeddingCerts" />
-    <public name="intro" />
-    <public name="enableOnBackInvokedCallback" />
-    <public name="supportsInlineSuggestionsWithTouchExploration" />
-    <public name="lineBreakStyle" />
-    <public name="lineBreakWordStyle" />
-    <!-- @hide -->
-    <public name="maxDrawableWidth" />
-    <!-- @hide -->
-    <public name="maxDrawableHeight" />
-    <public name="backdropColor" />
+  <staging-public-group type="attr" first-id="0x01ce0000">
   </staging-public-group>
 
-  <staging-public-group type="id" first-id="0x01de0000">
-    <public name="removed_accessibilityActionSwipeLeft" />
-    <public name="removed_accessibilityActionSwipeRight" />
-    <public name="removed_accessibilityActionSwipeUp" />
-    <public name="removed_accessibilityActionSwipeDown" />
-    <public name="accessibilityActionShowTextSuggestions" />
-    <public name="inputExtractAction" />
-    <public name="inputExtractAccessories" />
+  <staging-public-group type="id" first-id="0x01cd0000">
   </staging-public-group>
 
-  <staging-public-group type="style" first-id="0x01dd0000">
-    <public name="TextAppearance.DeviceDefault.Headline" />
+  <staging-public-group type="style" first-id="0x01cc0000">
   </staging-public-group>
 
-  <staging-public-group type="string" first-id="0x01dc0000">
-    <!-- @hide @SystemApi -->
-    <public name="config_systemSupervision" />
-    <!-- @hide @SystemApi -->
-    <public name="config_devicePolicyManagement" />
-    <!-- @hide @SystemApi -->
-    <public name="config_systemAppProtectionService" />
-    <!-- @hide @SystemApi @TestApi -->
-    <public name="config_systemAutomotiveCalendarSyncManager" />
-    <!-- @hide @SystemApi -->
-    <public name="config_defaultAutomotiveNavigation" />
-    <!-- @hide @SystemApi -->
-    <public name="safety_protection_display_text" />
-    <!-- @hide @SystemApi -->
-    <public name="config_systemSettingsIntelligence" />
-    <!-- @hide -->
-    <public name="config_systemBluetoothStack" />
+  <staging-public-group type="string" first-id="0x01cb0000">
   </staging-public-group>
 
-  <staging-public-group type="dimen" first-id="0x01db0000">
+  <staging-public-group type="dimen" first-id="0x01ca0000">
   </staging-public-group>
 
-  <staging-public-group type="color" first-id="0x01da0000">
+  <staging-public-group type="color" first-id="0x01c90000">
   </staging-public-group>
 
-  <staging-public-group type="array" first-id="0x01d90000">
-    <!-- @hide @SystemApi -->
-    <public name="config_optionalIpSecAlgorithms" />
+  <staging-public-group type="array" first-id="0x01c80000">
   </staging-public-group>
 
-  <staging-public-group type="drawable" first-id="0x01d80000">
-    <!-- @hide @SystemApi -->
-    <public name="ic_safety_protection" />
+  <staging-public-group type="drawable" first-id="0x01c70000">
   </staging-public-group>
 
-  <staging-public-group type="layout" first-id="0x01d70000">
+  <staging-public-group type="layout" first-id="0x01c60000">
   </staging-public-group>
 
-  <staging-public-group type="anim" first-id="0x01d60000">
+  <staging-public-group type="anim" first-id="0x01c50000">
   </staging-public-group>
 
-  <staging-public-group type="animator" first-id="0x01d50000">
+  <staging-public-group type="animator" first-id="0x01c40000">
   </staging-public-group>
 
-  <staging-public-group type="interpolator" first-id="0x01d40000">
+  <staging-public-group type="interpolator" first-id="0x01c30000">
   </staging-public-group>
 
-  <staging-public-group type="mipmap" first-id="0x01d30000">
+  <staging-public-group type="mipmap" first-id="0x01c20000">
   </staging-public-group>
 
-  <staging-public-group type="integer" first-id="0x01d20000">
+  <staging-public-group type="integer" first-id="0x01c10000">
   </staging-public-group>
 
-  <staging-public-group type="transition" first-id="0x01d10000">
+  <staging-public-group type="transition" first-id="0x01c00000">
   </staging-public-group>
 
-  <staging-public-group type="raw" first-id="0x01d00000">
+  <staging-public-group type="raw" first-id="0x01bf0000">
   </staging-public-group>
 
-  <staging-public-group type="bool" first-id="0x01cf0000">
-    <!-- @hide @TestApi -->
-    <public name="config_preventImeStartupUnlessTextEditor" />
-    <!-- @hide @SystemApi -->
-    <public name="config_enableQrCodeScannerOnLockScreen" />
+  <staging-public-group type="bool" first-id="0x01be0000">
   </staging-public-group>
 
-  <staging-public-group type="fraction" first-id="0x01ce0000">
+  <staging-public-group type="fraction" first-id="0x01bd0000">
   </staging-public-group>
 
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5f14647..a9b95da 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3604,6 +3604,7 @@
   <java-symbol type="dimen" name="notification_actions_icon_drawable_size"/>
   <java-symbol type="dimen" name="notification_custom_view_max_image_height"/>
   <java-symbol type="dimen" name="notification_custom_view_max_image_width"/>
+  <java-symbol type="dimen" name="notification_person_icon_max_size" />
 
   <java-symbol type="dimen" name="notification_small_icon_size_low_ram"/>
   <java-symbol type="dimen" name="notification_big_picture_max_height_low_ram"/>
@@ -3612,6 +3613,7 @@
   <java-symbol type="dimen" name="notification_grayscale_icon_max_size"/>
   <java-symbol type="dimen" name="notification_custom_view_max_image_height_low_ram"/>
   <java-symbol type="dimen" name="notification_custom_view_max_image_width_low_ram"/>
+  <java-symbol type="dimen" name="notification_person_icon_max_size_low_ram" />
 
   <!-- Accessibility fingerprint gestures -->
   <java-symbol type="string" name="capability_title_canCaptureFingerprintGestures" />
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index e6d2364..a5da442 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -521,6 +521,70 @@
     }
 
     @Test
+    public void testBuild_ensureMessagingUserIsNotTooBig_resizesIcon() {
+        Icon hugeIcon = Icon.createWithBitmap(
+                Bitmap.createBitmap(3000, 3000, Bitmap.Config.ARGB_8888));
+        Icon hugeMessageAvatar = Icon.createWithBitmap(
+                Bitmap.createBitmap(3000, 3000, Bitmap.Config.ARGB_8888));
+        Icon hugeHistoricMessageAvatar = Icon.createWithBitmap(
+                Bitmap.createBitmap(3000, 3000, Bitmap.Config.ARGB_8888));
+
+        Notification.MessagingStyle style = new Notification.MessagingStyle(
+                new Person.Builder().setIcon(hugeIcon).setName("A User").build());
+        style.addMessage(new Notification.MessagingStyle.Message("A message", 123456,
+                new Person.Builder().setIcon(hugeMessageAvatar).setName("A Sender").build()));
+        style.addHistoricMessage(new Notification.MessagingStyle.Message("A message", 123456,
+                new Person.Builder().setIcon(hugeHistoricMessageAvatar).setName(
+                        "A Historic Sender").build()));
+        Notification notification = new Notification.Builder(mContext, "Channel").setStyle(
+                style).build();
+
+        Bitmap personIcon = style.getUser().getIcon().getBitmap();
+        assertThat(personIcon.getWidth()).isEqualTo(
+                mContext.getResources().getDimensionPixelSize(
+                        R.dimen.notification_person_icon_max_size));
+        assertThat(personIcon.getHeight()).isEqualTo(
+                mContext.getResources().getDimensionPixelSize(
+                        R.dimen.notification_person_icon_max_size));
+
+        Bitmap avatarIcon = style.getMessages().get(0).getSenderPerson().getIcon().getBitmap();
+        assertThat(avatarIcon.getWidth()).isEqualTo(
+                mContext.getResources().getDimensionPixelSize(
+                        R.dimen.notification_person_icon_max_size));
+        assertThat(avatarIcon.getHeight()).isEqualTo(
+                mContext.getResources().getDimensionPixelSize(
+                        R.dimen.notification_person_icon_max_size));
+
+        Bitmap historicAvatarIcon = style.getHistoricMessages().get(
+                0).getSenderPerson().getIcon().getBitmap();
+        assertThat(historicAvatarIcon.getWidth()).isEqualTo(
+                mContext.getResources().getDimensionPixelSize(
+                        R.dimen.notification_person_icon_max_size));
+        assertThat(historicAvatarIcon.getHeight()).isEqualTo(
+                mContext.getResources().getDimensionPixelSize(
+                        R.dimen.notification_person_icon_max_size));
+    }
+
+    @Test
+    public void testBuild_ensureMessagingShortcutIconIsNotTooBig_resizesIcon() {
+        Icon hugeIcon = Icon.createWithBitmap(
+                Bitmap.createBitmap(3000, 3000, Bitmap.Config.ARGB_8888));
+        Notification.MessagingStyle style = new Notification.MessagingStyle(
+                new Person.Builder().setName("A User").build()).setShortcutIcon(hugeIcon);
+
+        Notification notification = new Notification.Builder(mContext, "Channel").setStyle(
+                style).build();
+        Bitmap shortcutIcon = style.getShortcutIcon().getBitmap();
+
+        assertThat(shortcutIcon.getWidth()).isEqualTo(
+                mContext.getResources().getDimensionPixelSize(
+                        R.dimen.notification_small_icon_size));
+        assertThat(shortcutIcon.getHeight()).isEqualTo(
+                mContext.getResources().getDimensionPixelSize(
+                        R.dimen.notification_small_icon_size));
+    }
+
+    @Test
     public void testColors_ensureColors_dayMode_producesValidPalette() {
         Notification.Colors c = new Notification.Colors();
         boolean colorized = false;
diff --git a/core/tests/coretests/src/android/os/IpcDataCacheTest.java b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
index fa7d721..34712ce 100644
--- a/core/tests/coretests/src/android/os/IpcDataCacheTest.java
+++ b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
@@ -53,6 +53,14 @@
             return value(x);
         }
 
+        // A single query but this can throw an exception.
+        boolean query(int x, boolean y) throws RemoteException {
+            if (y) {
+                throw new RemoteException();
+            }
+            return query(x);
+        }
+
         // Return the expected value of an input, without incrementing the query count.
         boolean value(int x) {
             return x % 3 == 0;
@@ -138,6 +146,47 @@
         tester.verify(9);
     }
 
+    // This test is disabled pending an sepolicy change that allows any app to set the
+    // test property.
+    @Test
+    public void testRemoteCall() {
+
+        // A stand-in for the binder.  The test verifies that calls are passed through to
+        // this class properly.
+        ServerProxy tester = new ServerProxy();
+
+        // Create a cache that uses simple arithmetic to computer its values.
+        IpcDataCache.Config config = new IpcDataCache.Config(4, MODULE, API, "testCache2");
+        IpcDataCache<Integer, Boolean> testCache =
+                new IpcDataCache<>(config, (x) -> tester.query(x, x % 10 == 9));
+
+        IpcDataCache.setTestMode(true);
+        testCache.testPropertyName();
+
+        tester.verify(0);
+        assertEquals(tester.value(3), testCache.query(3));
+        tester.verify(1);
+        assertEquals(tester.value(3), testCache.query(3));
+        tester.verify(2);
+        testCache.invalidateCache();
+        assertEquals(tester.value(3), testCache.query(3));
+        tester.verify(3);
+        assertEquals(tester.value(5), testCache.query(5));
+        tester.verify(4);
+        assertEquals(tester.value(5), testCache.query(5));
+        tester.verify(4);
+        assertEquals(tester.value(3), testCache.query(3));
+        tester.verify(4);
+
+        try {
+            testCache.query(9);
+            assertEquals(false, true);          // The code should not reach this point.
+        } catch (RuntimeException e) {
+            assertEquals(e.getCause() instanceof RemoteException, true);
+        }
+        tester.verify(4);
+    }
+
     @Test
     public void testDisableCache() {
 
@@ -225,6 +274,17 @@
             testPropertyName();
         }
 
+        TestCache(IpcDataCache.Config c) {
+            this(c, new TestQuery());
+        }
+
+        TestCache(IpcDataCache.Config c, TestQuery query) {
+            super(c, query);
+            mQuery = query;
+            setTestMode(true);
+            testPropertyName();
+        }
+
         int getRecomputeCount() {
             return mQuery.getRecomputeCount();
         }
@@ -309,4 +369,48 @@
         assertEquals("foo5", cache.query(5));
         assertEquals(3, cache.getRecomputeCount());
     }
+
+    @Test
+    public void testConfig() {
+        IpcDataCache.Config a = new IpcDataCache.Config(8, MODULE, "apiA");
+        TestCache ac = new TestCache(a);
+        assertEquals(8, a.maxEntries());
+        assertEquals(MODULE, a.module());
+        assertEquals("apiA", a.api());
+        assertEquals("apiA", a.name());
+        IpcDataCache.Config b = new IpcDataCache.Config(a, "apiB");
+        TestCache bc = new TestCache(b);
+        assertEquals(8, b.maxEntries());
+        assertEquals(MODULE, b.module());
+        assertEquals("apiB", b.api());
+        assertEquals("apiB", b.name());
+        IpcDataCache.Config c = new IpcDataCache.Config(a, "apiC", "nameC");
+        TestCache cc = new TestCache(c);
+        assertEquals(8, c.maxEntries());
+        assertEquals(MODULE, c.module());
+        assertEquals("apiC", c.api());
+        assertEquals("nameC", c.name());
+        IpcDataCache.Config d = a.child("nameD");
+        TestCache dc = new TestCache(d);
+        assertEquals(8, d.maxEntries());
+        assertEquals(MODULE, d.module());
+        assertEquals("apiA", d.api());
+        assertEquals("nameD", d.name());
+
+        a.disableForCurrentProcess();
+        assertEquals(ac.isDisabled(), true);
+        assertEquals(bc.isDisabled(), false);
+        assertEquals(cc.isDisabled(), false);
+        assertEquals(dc.isDisabled(), false);
+
+        a.disableAllForCurrentProcess();
+        assertEquals(ac.isDisabled(), true);
+        assertEquals(bc.isDisabled(), false);
+        assertEquals(cc.isDisabled(), false);
+        assertEquals(dc.isDisabled(), true);
+
+        IpcDataCache.Config e = a.child("nameE");
+        TestCache ec = new TestCache(e);
+        assertEquals(ec.isDisabled(), true);
+    }
 }
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index bf8bb76..be9da11 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -217,7 +217,8 @@
         mState.getSource(ITYPE_CAPTION_BAR).setVisible(true);
 
         Insets visibleInsets = mState.calculateVisibleInsets(
-                new Rect(0, 0, 100, 400), SOFT_INPUT_ADJUST_NOTHING);
+                new Rect(0, 0, 100, 400), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+                SOFT_INPUT_ADJUST_NOTHING, 0 /* windowFlags */);
         assertEquals(Insets.of(0, 300, 0, 0), visibleInsets);
     }
 
@@ -227,7 +228,8 @@
         mState.getSource(ITYPE_CAPTION_BAR).setVisible(true);
 
         Insets visibleInsets = mState.calculateVisibleInsets(
-                new Rect(0, 0, 150, 400), SOFT_INPUT_ADJUST_NOTHING);
+                new Rect(0, 0, 150, 400), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+                SOFT_INPUT_ADJUST_NOTHING, 0 /* windowFlags */);
         assertEquals(Insets.of(0, 300, 0, 0), visibleInsets);
     }
 
@@ -414,7 +416,8 @@
         mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
         mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
         Insets visibleInsets = mState.calculateVisibleInsets(
-                new Rect(0, 0, 100, 300), SOFT_INPUT_ADJUST_PAN);
+                new Rect(0, 0, 100, 300), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+                SOFT_INPUT_ADJUST_PAN, 0 /* windowFlags */);
         assertEquals(Insets.of(0, 100, 0, 100), visibleInsets);
     }
 
@@ -429,11 +432,28 @@
         mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
         mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
         Insets visibleInsets = mState.calculateVisibleInsets(
-                new Rect(0, 0, 100, 300), SOFT_INPUT_ADJUST_NOTHING);
+                new Rect(0, 0, 100, 300), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+                SOFT_INPUT_ADJUST_NOTHING, 0 /* windowFlags */);
         assertEquals(Insets.of(0, 100, 0, 0), visibleInsets);
     }
 
     @Test
+    public void testCalculateVisibleInsets_layoutNoLimits() {
+        mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+        mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+        mState.getSource(ITYPE_IME).setVisible(true);
+
+        // Make sure bottom gestures are ignored
+        mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
+        mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
+        Insets visibleInsets = mState.calculateVisibleInsets(
+                new Rect(0, 0, 100, 300), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+                SOFT_INPUT_ADJUST_PAN, FLAG_LAYOUT_NO_LIMITS);
+        assertEquals(Insets.NONE, visibleInsets);
+    }
+
+    @Test
     public void testCalculateUncontrollableInsets() {
         mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 200, 100));
         mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
diff --git a/core/tests/coretests/src/android/view/WindowInfoTest.java b/core/tests/coretests/src/android/view/WindowInfoTest.java
index 0a99b08..afc2c00 100644
--- a/core/tests/coretests/src/android/view/WindowInfoTest.java
+++ b/core/tests/coretests/src/android/view/WindowInfoTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.mock;
 
 import android.app.ActivityTaskManager;
+import android.graphics.Matrix;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
@@ -38,6 +39,7 @@
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 
 /**
  * Class for testing {@link WindowInfo}.
@@ -94,6 +96,8 @@
         assertFalse(w.inPictureInPicture);
         assertFalse(w.hasFlagWatchOutsideTouch);
         assertTrue(w.regionInScreen.isEmpty());
+        assertEquals(w.mTransformMatrix.length, 9);
+        assertTrue(w.mMagnificationSpec.isNop());
     }
 
     @SmallTest
@@ -117,6 +121,8 @@
         equality &= w1.parentToken == w2.parentToken;
         equality &= w1.activityToken == w2.activityToken;
         equality &= w1.regionInScreen.equals(w2.regionInScreen);
+        equality &= w1.mMagnificationSpec.equals(w2.mMagnificationSpec);
+        equality &= Arrays.equals(w1.mTransformMatrix, w2.mTransformMatrix);
         return equality;
     }
 
@@ -136,5 +142,9 @@
         windowInfo.inPictureInPicture = true;
         windowInfo.hasFlagWatchOutsideTouch = true;
         windowInfo.regionInScreen.set(0, 0, 1080, 1080);
+        windowInfo.mMagnificationSpec.scale = 2.0f;
+        windowInfo.mMagnificationSpec.offsetX = 100f;
+        windowInfo.mMagnificationSpec.offsetY = 200f;
+        Matrix.IDENTITY_MATRIX.getValues(windowInfo.mTransformMatrix);
     }
 }
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index c21aa2d..9b09616 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -703,12 +703,6 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
-    "-1427392850": {
-      "message": "WindowState: Setting back callback %s (priority: %d) (Client IWindow: %s). (WindowState: %s)",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_BACK_PREVIEW",
-      "at": "com\/android\/server\/wm\/WindowState.java"
-    },
     "-1427184084": {
       "message": "addWindow: New client %s: window=%s Callers=%s",
       "level": "VERBOSE",
@@ -859,6 +853,12 @@
       "group": "WM_DEBUG_ANIM",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
+    "-1277068810": {
+      "message": "startBackNavigation currentTask=%s, topRunningActivity=%s, callbackInfo=%s, currentFocus=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_BACK_PREVIEW",
+      "at": "com\/android\/server\/wm\/BackNavigationController.java"
+    },
     "-1270731689": {
       "message": "Attempted to set replacing window on app token with no content %s",
       "level": "WARN",
@@ -1099,12 +1099,6 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
-    "-1010850753": {
-      "message": "No focused window, defaulting to top task's window",
-      "level": "WARN",
-      "group": "WM_DEBUG_BACK_PREVIEW",
-      "at": "com\/android\/server\/wm\/BackNavigationController.java"
-    },
     "-1009117329": {
       "message": "isFetchingAppTransitionSpecs=true",
       "level": "VERBOSE",
@@ -3043,12 +3037,6 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
-    "878005951": {
-      "message": "startBackNavigation task=%s, topRunningActivity=%s, callbackInfo=%s, currentFocus=%s",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_BACK_PREVIEW",
-      "at": "com\/android\/server\/wm\/BackNavigationController.java"
-    },
     "892244061": {
       "message": "Waiting for drawn %s: removed=%b visible=%b mHasSurface=%b drawState=%d",
       "level": "INFO",
@@ -3331,12 +3319,6 @@
       "group": "WM_DEBUG_APP_TRANSITIONS",
       "at": "com\/android\/server\/wm\/DisplayContent.java"
     },
-    "1172542963": {
-      "message": "onBackNavigationDone backType=%s, task=%s, prevTaskTopActivity=%s",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_BACK_PREVIEW",
-      "at": "com\/android\/server\/wm\/BackNavigationController.java"
-    },
     "1175495463": {
       "message": "ImeContainer just became organized. Reparenting under parent. imeParentSurfaceControl=%s",
       "level": "INFO",
@@ -3409,6 +3391,12 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "1264179654": {
+      "message": "No focused window, defaulting to top current task's window",
+      "level": "WARN",
+      "group": "WM_DEBUG_BACK_PREVIEW",
+      "at": "com\/android\/server\/wm\/BackNavigationController.java"
+    },
     "1270792394": {
       "message": "Resumed after relaunch %s",
       "level": "DEBUG",
@@ -3427,6 +3415,12 @@
       "group": "WM_DEBUG_FOCUS_LIGHT",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
+    "1288920916": {
+      "message": "Error sending initial insets change to WindowContainer overlay",
+      "level": "ERROR",
+      "group": "WM_DEBUG_ANIM",
+      "at": "com\/android\/server\/wm\/WindowContainer.java"
+    },
     "1305412562": {
       "message": "Report configuration: %s %s",
       "level": "VERBOSE",
@@ -3853,6 +3847,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1778919449": {
+      "message": "onBackNavigationDone backType=%s, task=%s, prevActivity=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_BACK_PREVIEW",
+      "at": "com\/android\/server\/wm\/BackNavigationController.java"
+    },
     "1781673113": {
       "message": "onAnimationFinished(): targetRootTask=%s targetActivity=%s mRestoreTargetBehindRootTask=%s",
       "level": "DEBUG",
diff --git a/data/keyboards/Vendor_0e6f_Product_f501.kl b/data/keyboards/Vendor_0e6f_Product_f501.kl
new file mode 100644
index 0000000..b46c005
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_f501.kl
@@ -0,0 +1,55 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# XBox-compatible USB Controller
+#
+
+key 304   BUTTON_A
+key 305   BUTTON_B
+key 307   BUTTON_X
+key 308   BUTTON_Y
+key 310   BUTTON_L1
+key 311   BUTTON_R1
+
+key 317   BUTTON_THUMBL
+key 318   BUTTON_THUMBR
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+
+# Button labeled as "BACK" (left-pointing triangle)
+key 314   BUTTON_SELECT
+
+# The branded "X" button in the center of the controller
+key 316   BUTTON_MODE
+
+# Button labeled as "START" (right-pointing triangle)
+key 315   BUTTON_START
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 7f70e1c..4b723d1 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -47,7 +47,7 @@
     /** Create a new connection with the surface flinger. */
     public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
             @PixelFormat.Format int format) {
-        this(name, false /* updateDestinationFrame */);
+        this(name, true /* updateDestinationFrame */);
         update(sc, width, height, format);
     }
 
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 54bab4a..ffd041f6 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -1637,8 +1637,8 @@
          * Sets whether the keystore requires the screen to be unlocked before allowing decryption
          * using this key. If this is set to {@code true}, any attempt to decrypt or sign using this
          * key while the screen is locked will fail. A locked device requires a PIN, password,
-         * biometric, or other trusted factor to access. While the screen is locked, the key can
-         * still be used for encryption or signature verification.
+         * biometric, or other trusted factor to access. While the screen is locked, any associated
+         * public key can still be used (e.g for signature verification).
          */
         @NonNull
         public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) {
diff --git a/libs/WindowManager/Jetpack/src/TEST_MAPPING b/libs/WindowManager/Jetpack/src/TEST_MAPPING
new file mode 100644
index 0000000..eacfe25
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/TEST_MAPPING
@@ -0,0 +1,32 @@
+{
+  "presubmit": [
+    {
+      "name": "WMJetpackUnitTests",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    },
+    {
+      "name": "CtsWindowManagerJetpackTestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 3ec8843..33a41ec 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -155,13 +155,18 @@
         // Check if there are no running activities - consider the container empty if there are no
         // non-finishing activities left.
         if (!taskFragmentInfo.hasRunningActivity()) {
-            // TODO(b/225371112): Don't finish dependent if the last activity is moved to the PIP
-            // Task.
-            // Do not finish the dependents if this TaskFragment was cleared due to launching
-            // activity in the Task.
-            final boolean shouldFinishDependent =
-                    !taskFragmentInfo.isTaskClearedForReuse();
-            mPresenter.cleanupContainer(container, shouldFinishDependent);
+            if (taskFragmentInfo.isTaskFragmentClearedForPip()) {
+                // Do not finish the dependents if the last activity is reparented to PiP.
+                // Instead, the original split should be cleanup, and the dependent may be expanded
+                // to fullscreen.
+                cleanupForEnterPip(container);
+                mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
+            } else {
+                // Do not finish the dependents if this TaskFragment was cleared due to launching
+                // activity in the Task.
+                final boolean shouldFinishDependent = !taskFragmentInfo.isTaskClearedForReuse();
+                mPresenter.cleanupContainer(container, shouldFinishDependent);
+            }
         } else if (wasInPip && isInPip) {
             // No update until exit PIP.
             return;
diff --git a/libs/WindowManager/Jetpack/tests/unittest/Android.bp b/libs/WindowManager/Jetpack/tests/unittest/Android.bp
index 212fbd0..b6e743a 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/Android.bp
+++ b/libs/WindowManager/Jetpack/tests/unittest/Android.bp
@@ -23,6 +23,8 @@
 
 android_test {
     name: "WMJetpackUnitTests",
+    // To make the test run via TEST_MAPPING
+    test_suites: ["device-tests"],
 
     srcs: [
         "**/*.java",
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
index b6df876..13a2c78 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
@@ -18,6 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.platform.test.annotations.Presubmit;
+
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
@@ -25,6 +27,13 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+/**
+ * Test class for {@link WindowExtensionsTest}.
+ *
+ * Build/Install/Run:
+ *  atest WMJetpackUnitTests:WindowExtensionsTest
+ */
+@Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class WindowExtensionsTest {
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
index 26463c1..b06ce4c 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
@@ -24,6 +24,8 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.never;
 
+import android.platform.test.annotations.Presubmit;
+
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
@@ -39,6 +41,7 @@
  * Build/Install/Run:
  *  atest WMJetpackUnitTests:JetpackTaskFragmentOrganizerTest
  */
+@Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class JetpackTaskFragmentOrganizerTest {
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index 120c7eb..a26a4b6 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -23,6 +23,8 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
+import android.platform.test.annotations.Presubmit;
+
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 import androidx.window.extensions.embedding.SplitController.TaskContainer;
@@ -37,6 +39,7 @@
  * Build/Install/Run:
  *  atest WMJetpackUnitTests:SplitController
  */
+@Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class SplitControllerTest {
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java
index 7f88f4e..af3ad70 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.never;
 
+import android.platform.test.annotations.Presubmit;
 import android.window.TaskFragmentOrganizer;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -38,6 +39,7 @@
  * Build/Install/Run:
  *  atest WMJetpackUnitTests:TaskFragmentAnimationControllerTest
  */
+@Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class TaskFragmentAnimationControllerTest {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 1a1cd5b..b6fb828 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1225,12 +1225,6 @@
                 mOverflowListener.applyUpdate(update);
             }
 
-            // Collapsing? Do this first before remaining steps.
-            if (update.expandedChanged && !update.expanded) {
-                mStackView.setExpanded(false);
-                mSysuiProxy.requestNotificationShadeTopUi(false, TAG);
-            }
-
             // Do removals, if any.
             ArrayList<Pair<Bubble, Integer>> removedBubbles =
                     new ArrayList<>(update.removedBubbles);
@@ -1307,6 +1301,11 @@
                 mStackView.updateBubbleOrder(update.bubbles);
             }
 
+            if (update.expandedChanged && !update.expanded) {
+                mStackView.setExpanded(false);
+                mSysuiProxy.requestNotificationShadeTopUi(false, TAG);
+            }
+
             if (update.selectionChanged && mStackView != null) {
                 mStackView.setSelectedBubble(update.selectedBubble);
                 if (update.selectedBubble != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index f60b659..322c0bf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -913,8 +913,10 @@
                             afterExpandedViewAnimation();
                             showManageMenu(mShowingManage);
                         } /* after */);
+                        PointF p = mPositioner.getExpandedBubbleXY(getBubbleIndex(mExpandedBubble),
+                                getState());
                         final float translationY = mPositioner.getExpandedViewY(mExpandedBubble,
-                                getBubbleIndex(mExpandedBubble));
+                                mPositioner.showBubblesVertically() ? p.y : p.x);
                         mExpandedViewContainer.setTranslationX(0f);
                         mExpandedViewContainer.setTranslationY(translationY);
                         mExpandedViewContainer.setAlpha(1f);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index 6f4e22f..79d795e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -32,6 +32,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
@@ -336,6 +337,12 @@
         return navigationBarPosition(res, mWidth, mHeight, mRotation);
     }
 
+    /** @return {@link DisplayCutout} instance. */
+    @Nullable
+    public DisplayCutout getDisplayCutout() {
+        return mCutout;
+    }
+
     /**
      * Calculates the stable insets if we already have the non-decor insets.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
index e28c58c..9754a03 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
@@ -37,7 +37,6 @@
 import android.graphics.Region.Op;
 import android.hardware.display.DisplayManager;
 import android.os.Bundle;
-import android.os.RemoteException;
 import android.util.AttributeSet;
 import android.util.Slog;
 import android.view.Choreographer;
@@ -241,22 +240,6 @@
         }
     };
 
-    private Runnable mUpdateEmbeddedMatrix = () -> {
-        if (getViewRootImpl() == null) {
-            return;
-        }
-        if (isHorizontalDivision()) {
-            mTmpMatrix.setTranslate(0, mDividerPositionY - mDividerInsets);
-        } else {
-            mTmpMatrix.setTranslate(mDividerPositionX - mDividerInsets, 0);
-        }
-        mTmpMatrix.getValues(mTmpValues);
-        try {
-            getViewRootImpl().getAccessibilityEmbeddedConnection().setScreenMatrix(mTmpValues);
-        } catch (RemoteException e) {
-        }
-    };
-
     public DividerView(Context context) {
         this(context, null);
     }
@@ -1045,10 +1028,6 @@
                 t.setPosition(dividerCtrl, mDividerPositionX - mDividerInsets, 0);
             }
         }
-        if (getViewRootImpl() != null) {
-            getHandler().removeCallbacks(mUpdateEmbeddedMatrix);
-            getHandler().post(mUpdateEmbeddedMatrix);
-        }
     }
 
     void setResizeDimLayer(Transaction t, boolean primary, float alpha) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 3379000..95bb65c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -250,7 +250,6 @@
         protected T mCurrentValue;
         protected T mStartValue;
         private T mEndValue;
-        private float mStartingAngle;
         private PipAnimationCallback mPipAnimationCallback;
         private PipTransactionHandler mPipTransactionHandler;
         private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
@@ -260,8 +259,8 @@
         protected SurfaceControl mContentOverlay;
 
         private PipTransitionAnimator(TaskInfo taskInfo, SurfaceControl leash,
-                @AnimationType int animationType, Rect destinationBounds, T baseValue, T startValue,
-                T endValue, float startingAngle) {
+                @AnimationType int animationType,
+                Rect destinationBounds, T baseValue, T startValue, T endValue) {
             mTaskInfo = taskInfo;
             mLeash = leash;
             mAnimationType = animationType;
@@ -269,7 +268,6 @@
             mBaseValue = baseValue;
             mStartValue = startValue;
             mEndValue = endValue;
-            mStartingAngle = startingAngle;
             addListener(this);
             addUpdateListener(this);
             mSurfaceControlTransactionFactory =
@@ -480,7 +478,7 @@
         static PipTransitionAnimator<Float> ofAlpha(TaskInfo taskInfo, SurfaceControl leash,
                 Rect destinationBounds, float startValue, float endValue) {
             return new PipTransitionAnimator<Float>(taskInfo, leash, ANIM_TYPE_ALPHA,
-                    destinationBounds, startValue, startValue, endValue, 0) {
+                    destinationBounds, startValue, startValue, endValue) {
                 @Override
                 void applySurfaceControlTransaction(SurfaceControl leash,
                         SurfaceControl.Transaction tx, float fraction) {
@@ -520,7 +518,7 @@
                 @PipAnimationController.TransitionDirection int direction, float startingAngle,
                 @Surface.Rotation int rotationDelta) {
             final boolean isOutPipDirection = isOutPipDirection(direction);
-
+            final boolean isInPipDirection = isInPipDirection(direction);
             // Just for simplicity we'll interpolate between the source rect hint insets and empty
             // insets to calculate the window crop
             final Rect initialSourceValue;
@@ -559,8 +557,7 @@
 
             // construct new Rect instances in case they are recycled
             return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS,
-                    endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue),
-                    startingAngle) {
+                    endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue)) {
                 private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect());
                 private final RectEvaluator mInsetsEvaluator = new RectEvaluator(new Rect());
 
@@ -595,7 +592,8 @@
                     } else {
                         final Rect insets = computeInsets(fraction);
                         getSurfaceTransactionHelper().scaleAndCrop(tx, leash,
-                                initialSourceValue, bounds, insets);
+                                sourceHintRect, initialSourceValue, bounds, insets,
+                                isInPipDirection);
                         if (shouldApplyCornerRadius()) {
                             final Rect sourceBounds = new Rect(initialContainerRect);
                             sourceBounds.inset(insets);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index 00f62d4..b349010 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -103,21 +103,31 @@
      * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
      */
     public PipSurfaceTransactionHelper scaleAndCrop(SurfaceControl.Transaction tx,
-            SurfaceControl leash,
-            Rect sourceBounds, Rect destinationBounds, Rect insets) {
+            SurfaceControl leash, Rect sourceRectHint,
+            Rect sourceBounds, Rect destinationBounds, Rect insets,
+            boolean isInPipDirection) {
         mTmpSourceRectF.set(sourceBounds);
         mTmpDestinationRect.set(sourceBounds);
         mTmpDestinationRect.inset(insets);
         // Scale by the shortest edge and offset such that the top/left of the scaled inset source
         // rect aligns with the top/left of the destination bounds
-        final float scale = sourceBounds.width() <= sourceBounds.height()
-                ? (float) destinationBounds.width() / sourceBounds.width()
-                : (float) destinationBounds.height() / sourceBounds.height();
+        final float scale;
+        if (isInPipDirection
+                && sourceRectHint != null && sourceRectHint.width() < sourceBounds.width()) {
+            // scale by sourceRectHint if it's not edge-to-edge, for entering PiP transition only.
+            scale = sourceBounds.width() <= sourceBounds.height()
+                    ? (float) destinationBounds.width() / sourceRectHint.width()
+                    : (float) destinationBounds.height() / sourceRectHint.height();
+        } else {
+            scale = sourceBounds.width() <= sourceBounds.height()
+                    ? (float) destinationBounds.width() / sourceBounds.width()
+                    : (float) destinationBounds.height() / sourceBounds.height();
+        }
         final float left = destinationBounds.left - insets.left * scale;
         final float top = destinationBounds.top - insets.top * scale;
         mTmpTransform.setScale(scale, scale);
         tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
-                .setWindowCrop(leash, mTmpDestinationRect)
+                .setCrop(leash, mTmpDestinationRect)
                 .setPosition(leash, left, top);
         return this;
     }
@@ -163,7 +173,7 @@
         mTmpTransform.setScale(scale, scale);
         mTmpTransform.postRotate(degrees);
         mTmpTransform.postTranslate(positionX, positionY);
-        tx.setMatrix(leash, mTmpTransform, mTmpFloat9).setWindowCrop(leash, crop);
+        tx.setMatrix(leash, mTmpTransform, mTmpFloat9).setCrop(leash, crop);
         return this;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 5e4c12e..f73b81e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -135,19 +135,6 @@
         }
     };
 
-    private final float[] mTmpValues = new float[9];
-    private final Runnable mUpdateEmbeddedMatrix = () -> {
-        if (mPipMenuView == null || mPipMenuView.getViewRootImpl() == null) {
-            return;
-        }
-        mMoveTransform.getValues(mTmpValues);
-        try {
-            mPipMenuView.getViewRootImpl().getAccessibilityEmbeddedConnection()
-                    .setScreenMatrix(mTmpValues);
-        } catch (RemoteException e) {
-        }
-    };
-
     public PhonePipMenuController(Context context, PipBoundsState pipBoundsState,
             PipMediaController mediaController, SystemWindows systemWindows,
             Optional<SplitScreenController> splitScreenOptional,
@@ -348,11 +335,6 @@
         } else {
             mApplier.scheduleApply(params);
         }
-
-        if (mPipMenuView.getViewRootImpl() != null) {
-            mPipMenuView.getHandler().removeCallbacks(mUpdateEmbeddedMatrix);
-            mPipMenuView.getHandler().post(mUpdateEmbeddedMatrix);
-        }
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
index 69ae45d..7365b95 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
@@ -285,7 +285,7 @@
                 Region bounds, int interactionId,
                 IAccessibilityInteractionConnectionCallback callback, int flags,
                 int interrogatingPid, long interrogatingTid, MagnificationSpec spec,
-                Bundle arguments) throws RemoteException {
+                float[] matrixValues, Bundle arguments) throws RemoteException {
             mMainExcutor.execute(() -> {
                 PipAccessibilityInteractionConnection.this
                         .findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId, bounds,
@@ -298,7 +298,8 @@
         public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, String viewId,
                 Region bounds, int interactionId,
                 IAccessibilityInteractionConnectionCallback callback, int flags,
-                int interrogatingPid, long interrogatingTid, MagnificationSpec spec)
+                int interrogatingPid, long interrogatingTid, MagnificationSpec spec,
+                float[] matrixValues)
                 throws RemoteException {
             mMainExcutor.execute(() -> {
                 PipAccessibilityInteractionConnection.this.findAccessibilityNodeInfosByViewId(
@@ -311,7 +312,8 @@
         public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
                 Region bounds, int interactionId,
                 IAccessibilityInteractionConnectionCallback callback, int flags,
-                int interrogatingPid, long interrogatingTid, MagnificationSpec spec)
+                int interrogatingPid, long interrogatingTid, MagnificationSpec spec,
+                float[] matrixValues)
                 throws RemoteException {
             mMainExcutor.execute(() -> {
                 PipAccessibilityInteractionConnection.this.findAccessibilityNodeInfosByText(
@@ -323,7 +325,8 @@
         @Override
         public void findFocus(long accessibilityNodeId, int focusType, Region bounds,
                 int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
-                int interrogatingPid, long interrogatingTid, MagnificationSpec spec)
+                int interrogatingPid, long interrogatingTid, MagnificationSpec spec,
+                float[] matrixValues)
                 throws RemoteException {
             mMainExcutor.execute(() -> {
                 PipAccessibilityInteractionConnection.this.findFocus(accessibilityNodeId, focusType,
@@ -335,7 +338,8 @@
         @Override
         public void focusSearch(long accessibilityNodeId, int direction, Region bounds,
                 int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
-                int interrogatingPid, long interrogatingTid, MagnificationSpec spec)
+                int interrogatingPid, long interrogatingTid, MagnificationSpec spec,
+                float[] matrixValues)
                 throws RemoteException {
             mMainExcutor.execute(() -> {
                 PipAccessibilityInteractionConnection.this.focusSearch(accessibilityNodeId,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 147a272..ac7b9033b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -36,6 +36,7 @@
 import android.graphics.Rect;
 import android.provider.DeviceConfig;
 import android.util.Size;
+import android.view.DisplayCutout;
 import android.view.InputEvent;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
@@ -959,21 +960,38 @@
         }
 
         private boolean shouldStash(PointF vel, Rect motionBounds) {
+            final boolean flingToLeft = vel.x < -mStashVelocityThreshold;
+            final boolean flingToRight = vel.x > mStashVelocityThreshold;
+            final int offset = motionBounds.width() / 2;
+            final boolean droppingOnLeft =
+                    motionBounds.left < mPipBoundsState.getDisplayBounds().left - offset;
+            final boolean droppingOnRight =
+                    motionBounds.right > mPipBoundsState.getDisplayBounds().right + offset;
+
+            // Do not allow stash if the destination edge contains display cutout. We only
+            // compare the left and right edges since we do not allow stash on top / bottom.
+            final DisplayCutout displayCutout =
+                    mPipBoundsState.getDisplayLayout().getDisplayCutout();
+            if (displayCutout != null) {
+                if ((flingToLeft || droppingOnLeft)
+                        && !displayCutout.getBoundingRectLeft().isEmpty()) {
+                    return false;
+                } else if ((flingToRight || droppingOnRight)
+                        && !displayCutout.getBoundingRectRight().isEmpty()) {
+                    return false;
+                }
+            }
+
             // If user flings the PIP window above the minimum velocity, stash PIP.
             // Only allow stashing to the edge if PIP wasn't previously stashed on the opposite
             // edge.
-            final boolean stashFromFlingToEdge = ((vel.x < -mStashVelocityThreshold
-                    && mPipBoundsState.getStashedState() != STASH_TYPE_RIGHT)
-                    || (vel.x > mStashVelocityThreshold
-                    && mPipBoundsState.getStashedState() != STASH_TYPE_LEFT));
+            final boolean stashFromFlingToEdge =
+                    (flingToLeft && mPipBoundsState.getStashedState() != STASH_TYPE_RIGHT)
+                    || (flingToRight && mPipBoundsState.getStashedState() != STASH_TYPE_LEFT);
 
             // If User releases the PIP window while it's out of the display bounds, put
             // PIP into stashed mode.
-            final int offset = motionBounds.width() / 2;
-            final boolean stashFromDroppingOnEdge =
-                    (motionBounds.right > mPipBoundsState.getDisplayBounds().right + offset
-                            || motionBounds.left
-                            < mPipBoundsState.getDisplayBounds().left - offset);
+            final boolean stashFromDroppingOnEdge = droppingOnLeft || droppingOnRight;
 
             return stashFromFlingToEdge || stashFromDroppingOnEdge;
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index 7b8dcf7..2d67254 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -30,7 +30,6 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Handler;
-import android.os.RemoteException;
 import android.view.LayoutInflater;
 import android.view.SurfaceControl;
 import android.view.SyncRtSurfaceTransactionApplier;
@@ -88,19 +87,6 @@
     RectF mTmpDestinationRectF = new RectF();
     Matrix mMoveTransform = new Matrix();
 
-    private final float[] mTmpValues = new float[9];
-    private final Runnable mUpdateEmbeddedMatrix = () -> {
-        if (mPipMenuView == null || mPipMenuView.getViewRootImpl() == null) {
-            return;
-        }
-        mMoveTransform.getValues(mTmpValues);
-        try {
-            mPipMenuView.getViewRootImpl().getAccessibilityEmbeddedConnection()
-                    .setScreenMatrix(mTmpValues);
-        } catch (RemoteException e) {
-            if (DEBUG) e.printStackTrace();
-        }
-    };
     private final Runnable mCloseEduTextRunnable = this::closeEduText;
 
     public TvPipMenuController(Context context, TvPipBoundsState tvPipBoundsState,
@@ -525,11 +511,6 @@
             mApplier.scheduleApply(frontParams);
         }
 
-        if (mPipMenuView.getViewRootImpl() != null) {
-            mPipMenuView.getHandler().removeCallbacks(mUpdateEmbeddedMatrix);
-            mPipMenuView.getHandler().post(mUpdateEmbeddedMatrix);
-        }
-
         updateMenuBounds(pipDestBounds);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index aec51ba..10dfdc3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -190,7 +190,7 @@
             mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
                     mTaskOrganizer, mDisplayController, mDisplayImeController,
                     mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
-                    mIconProvider, mRecentTasksOptional, mUnfoldControllerProvider);
+                    mIconProvider, mMainExecutor, mRecentTasksOptional, mUnfoldControllerProvider);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 45931de..9d6e34d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -94,6 +94,7 @@
 import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.common.split.SplitLayout;
@@ -157,6 +158,7 @@
     private final TransactionPool mTransactionPool;
     private final SplitScreenTransitions mSplitTransitions;
     private final SplitscreenEventLogger mLogger;
+    private final ShellExecutor mMainExecutor;
     private final Optional<RecentTasksController> mRecentTasks;
 
     /**
@@ -196,13 +198,15 @@
             DisplayImeController displayImeController,
             DisplayInsetsController displayInsetsController, Transitions transitions,
             TransactionPool transactionPool, SplitscreenEventLogger logger,
-            IconProvider iconProvider, Optional<RecentTasksController> recentTasks,
+            IconProvider iconProvider, ShellExecutor mainExecutor,
+            Optional<RecentTasksController> recentTasks,
             Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
         mContext = context;
         mDisplayId = displayId;
         mSyncQueue = syncQueue;
         mTaskOrganizer = taskOrganizer;
         mLogger = logger;
+        mMainExecutor = mainExecutor;
         mRecentTasks = recentTasks;
         mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
         mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
@@ -247,7 +251,7 @@
             DisplayController displayController, DisplayImeController displayImeController,
             DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
             Transitions transitions, TransactionPool transactionPool,
-            SplitscreenEventLogger logger,
+            SplitscreenEventLogger logger, ShellExecutor mainExecutor,
             Optional<RecentTasksController> recentTasks,
             Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
         mContext = context;
@@ -266,6 +270,7 @@
         mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
         mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
         mLogger = logger;
+        mMainExecutor = mainExecutor;
         mRecentTasks = recentTasks;
         mDisplayController.addDisplayWindowListener(this);
         mDisplayLayout = new DisplayLayout();
@@ -394,6 +399,7 @@
             @Nullable PendingIntent pendingIntent, @Nullable Intent fillInIntent,
             @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
             @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
+        final boolean withIntent = pendingIntent != null && fillInIntent != null;
         // Init divider first to make divider leash for remote animation target.
         mSplitLayout.init();
         // Set false to avoid record new bounds with old task still on top;
@@ -423,10 +429,7 @@
                         new IRemoteAnimationFinishedCallback.Stub() {
                             @Override
                             public void onAnimationFinished() throws RemoteException {
-                                mIsDividerRemoteAnimating = false;
-                                mShouldUpdateRecents = true;
-                                mSyncQueue.queue(evictWct);
-                                mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
+                                onRemoteAnimationFinishedOrCancelled(evictWct);
                                 finishedCallback.onAnimationFinished();
                             }
                         };
@@ -447,10 +450,7 @@
 
             @Override
             public void onAnimationCancelled() {
-                mIsDividerRemoteAnimating = false;
-                mShouldUpdateRecents = true;
-                mSyncQueue.queue(evictWct);
-                mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
+                onRemoteAnimationFinishedOrCancelled(evictWct);
                 try {
                     adapter.getRunner().onAnimationCancelled();
                 } catch (RemoteException e) {
@@ -486,20 +486,37 @@
         addActivityOptions(sideOptions, mSideStage);
 
         // Add task launch requests
-        if (pendingIntent != null && fillInIntent != null) {
-            wct.startTask(mainTaskId, mainOptions);
+        wct.startTask(mainTaskId, mainOptions);
+        if (withIntent) {
             wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions);
         } else {
-            wct.startTask(mainTaskId, mainOptions);
             wct.startTask(sideTaskId, sideOptions);
         }
-
         // Using legacy transitions, so we can't use blast sync since it conflicts.
         mTaskOrganizer.applyTransaction(wct);
         mSyncQueue.runInSync(t ->
                 updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */));
     }
 
+    private void onRemoteAnimationFinishedOrCancelled(WindowContainerTransaction evictWct) {
+        mIsDividerRemoteAnimating = false;
+        mShouldUpdateRecents = true;
+        // If any stage has no child after animation finished, it means that split will display
+        // nothing, such status will happen if task and intent is same app but not support
+        // multi-instagce, we should exit split and expand that app as full screen.
+        if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
+            mMainExecutor.execute(() ->
+                    exitSplitScreen(mMainStage.getChildCount() == 0
+                        ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
+        } else {
+            mSyncQueue.queue(evictWct);
+            mSyncQueue.runInSync(t -> {
+                setDividerVisibility(true, t);
+                updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
+            });
+        }
+    }
+
     /**
      * Collects all the current child tasks of a specific split and prepares transaction to evict
      * them to display.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index 274d34b..0640ac5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -26,9 +26,7 @@
 import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
 import org.junit.FixMethodOrder
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
@@ -57,8 +55,6 @@
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Group3
 class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
-    @get:Rule
-    val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec)
 
     /**
      * Defines the transition used to run the test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index 9a8c894..8da6224 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -25,9 +25,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
 import org.junit.FixMethodOrder
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
@@ -57,8 +55,6 @@
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Group3
 class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransition(testSpec) {
-    @get:Rule
-    val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec)
 
     /**
      * Defines the transition used to run the test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
index 9c095a2..437ad89 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
@@ -25,9 +25,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
 import org.junit.FixMethodOrder
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
@@ -57,8 +55,6 @@
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Group3
 class ExitPipWithDismissButtonTest(testSpec: FlickerTestParameter) : ExitPipTransition(testSpec) {
-    @get:Rule
-    val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec)
 
     override val transition: FlickerBuilder.() -> Unit
         get() = {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index 49f36a4..eb9d3a1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -31,6 +31,7 @@
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.common.split.SplitLayout;
@@ -72,11 +73,13 @@
                 DisplayController displayController, DisplayImeController imeController,
                 DisplayInsetsController insetsController, SplitLayout splitLayout,
                 Transitions transitions, TransactionPool transactionPool,
-                SplitscreenEventLogger logger, Optional<RecentTasksController> recentTasks,
+                SplitscreenEventLogger logger, ShellExecutor mainExecutor,
+                Optional<RecentTasksController> recentTasks,
                 Provider<Optional<StageTaskUnfoldController>> unfoldController) {
             super(context, displayId, syncQueue, taskOrganizer, mainStage,
                     sideStage, displayController, imeController, insetsController, splitLayout,
-                    transitions, transactionPool, logger, recentTasks, unfoldController);
+                    transitions, transactionPool, logger, mainExecutor, recentTasks,
+                    unfoldController);
 
             // Prepare root task for testing.
             mRootTask = new TestRunningTaskInfoBuilder().build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index f847e6e..a55f737 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -97,6 +97,7 @@
     @Mock private SurfaceSession mSurfaceSession;
     @Mock private SplitscreenEventLogger mLogger;
     @Mock private IconProvider mIconProvider;
+    @Mock private ShellExecutor mMainExecutor;
     private SplitLayout mSplitLayout;
     private MainStage mMainStage;
     private SideStage mSideStage;
@@ -126,7 +127,7 @@
         mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
                 mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController,
                 mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
-                mTransactionPool, mLogger, Optional.empty(), Optional::empty);
+                mTransactionPool, mLogger, mMainExecutor, Optional.empty(), Optional::empty);
         mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
         doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
                 .when(mTransitions).startTransition(anyInt(), any(), any());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index c571d44..42d998f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -54,6 +54,7 @@
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.common.split.SplitLayout;
@@ -101,6 +102,8 @@
     private TransactionPool mTransactionPool;
     @Mock
     private SplitscreenEventLogger mLogger;
+    @Mock
+    private ShellExecutor mMainExecutor;
 
     private final Rect mBounds1 = new Rect(10, 20, 30, 40);
     private final Rect mBounds2 = new Rect(5, 10, 15, 20);
@@ -116,7 +119,7 @@
         mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
                 mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
                 mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool, mLogger,
-                Optional.empty(), new UnfoldControllerProvider()));
+                mMainExecutor, Optional.empty(), new UnfoldControllerProvider()));
         doNothing().when(mStageCoordinator).updateActivityOptions(any(), anyInt());
 
         when(mSplitLayout.getBounds1()).thenReturn(mBounds1);
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index ece150a..d8b077b 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -249,6 +249,7 @@
         "apex/android_matrix.cpp",
         "apex/android_paint.cpp",
         "apex/android_region.cpp",
+        "apex/properties.cpp",
     ],
 
     header_libs: ["android_graphics_apex_headers"],
diff --git a/libs/hwui/apex/include/android/graphics/properties.h b/libs/hwui/apex/include/android/graphics/properties.h
new file mode 100644
index 0000000..f24f840
--- /dev/null
+++ b/libs/hwui/apex/include/android/graphics/properties.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GRAPHICS_PROPERTIES_H
+#define ANDROID_GRAPHICS_PROPERTIES_H
+
+#include <cutils/compiler.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ * Returns true if libhwui is using the vulkan backend.
+ */
+ANDROID_API bool hwui_uses_vulkan();
+
+__END_DECLS
+
+#endif  // ANDROID_GRAPHICS_PROPERTIES_H
diff --git a/libs/hwui/apex/properties.cpp b/libs/hwui/apex/properties.cpp
new file mode 100644
index 0000000..abb333b
--- /dev/null
+++ b/libs/hwui/apex/properties.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android/graphics/properties.h"
+
+#include <Properties.h>
+
+bool hwui_uses_vulkan() {
+    return android::uirenderer::Properties::peekRenderPipelineType() ==
+           android::uirenderer::RenderPipelineType::SkiaVulkan;
+}
diff --git a/libs/hwui/libhwui.map.txt b/libs/hwui/libhwui.map.txt
index 087c006..fdb2373 100644
--- a/libs/hwui/libhwui.map.txt
+++ b/libs/hwui/libhwui.map.txt
@@ -39,6 +39,7 @@
     ARegionIterator_next;
     ARegionIterator_getRect;
     ARegionIterator_getTotalBounds;
+    hwui_uses_vulkan;
   local:
     *;
 };
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index ab00dd5..dc72aea 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -61,6 +61,17 @@
         return;
     }
 
+    // canvas may be an AlphaFilterCanvas, which is intended to draw with a
+    // modified alpha. We do not have a way to do this without drawing into an
+    // extra layer, which would have a performance cost. Draw directly into the
+    // underlying gpu canvas. This matches prior behavior and the behavior in
+    // Vulkan.
+    {
+        auto* gpuCanvas = SkAndroidFrameworkUtils::getBaseWrappedCanvas(canvas);
+        LOG_ALWAYS_FATAL_IF(!gpuCanvas, "GLFunctorDrawable::onDraw is using an invalid canvas!");
+        canvas = gpuCanvas;
+    }
+
     // flush will create a GrRenderTarget if not already present.
     canvas->flush();
 
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 5283889..8be1bcd 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -187,22 +187,14 @@
      */
     @SuppressWarnings("unused")
     @UnsupportedAppUsage
-    private long mNativeRecorderInJavaObj;
+    private long mNativeAudioRecordHandle;
 
     /**
      * Accessed by native methods: provides access to the callback data.
      */
     @SuppressWarnings("unused")
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private long mNativeCallbackCookie;
-
-    /**
-     * Accessed by native methods: provides access to the JNIDeviceCallback instance.
-     */
-    @SuppressWarnings("unused")
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private long mNativeDeviceCallback;
-
+    private long mNativeJNIDataHandle;
 
     //---------------------------------------------------------
     // Member variables
@@ -492,9 +484,8 @@
      * value here as no error checking is or can be done.
      */
     /*package*/ AudioRecord(long nativeRecordInJavaObj) {
-        mNativeRecorderInJavaObj = 0;
-        mNativeCallbackCookie = 0;
-        mNativeDeviceCallback = 0;
+        mNativeAudioRecordHandle = 0;
+        mNativeJNIDataHandle = 0;
 
         // other initialization...
         if (nativeRecordInJavaObj != 0) {
@@ -2131,7 +2122,7 @@
      * @hide
      */
     public int getPortId() {
-        if (mNativeRecorderInJavaObj == 0) {
+        if (mNativeAudioRecordHandle == 0) {
             return 0;
         }
         try {
diff --git a/omapi/java/android/se/omapi/SEService.java b/omapi/java/android/se/omapi/SEService.java
index f42ca36..306c09a 100644
--- a/omapi/java/android/se/omapi/SEService.java
+++ b/omapi/java/android/se/omapi/SEService.java
@@ -118,6 +118,16 @@
                 });
             }
         }
+
+        @Override
+        public String getInterfaceHash() {
+            return ISecureElementListener.HASH;
+        }
+
+        @Override
+        public int getInterfaceVersion() {
+            return ISecureElementListener.VERSION;
+        }
     }
     private SEListener mSEListener = new SEListener();
 
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index 62e53d6..a41399f 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -16,17 +16,18 @@
 
 package com.android.dynsystem;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.gsi.AvbPublicKey;
 import android.gsi.IGsiService;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Build;
-import android.os.MemoryFile;
-import android.os.ParcelFileDescriptor;
+import android.os.SharedMemory;
 import android.os.SystemProperties;
 import android.os.image.DynamicSystemManager;
 import android.service.persistentdata.PersistentDataBlockManager;
+import android.system.ErrnoException;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Range;
@@ -39,6 +40,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
+import java.nio.ByteBuffer;
 import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.List;
@@ -154,6 +156,22 @@
         void onResult(int resultCode, Throwable detail);
     }
 
+    private static class MappedMemoryBuffer implements AutoCloseable {
+        public ByteBuffer mBuffer;
+
+        MappedMemoryBuffer(@NonNull ByteBuffer buffer) {
+            mBuffer = buffer;
+        }
+
+        @Override
+        public void close() {
+            if (mBuffer != null) {
+                SharedMemory.unmap(mBuffer);
+                mBuffer = null;
+            }
+        }
+    }
+
     private final int mSharedMemorySize;
     private final String mUrl;
     private final String mDsuSlot;
@@ -674,59 +692,66 @@
 
         Log.d(TAG, "Start installing: " + partitionName);
 
-        MemoryFile memoryFile = new MemoryFile("dsu_" + partitionName, mSharedMemorySize);
-        ParcelFileDescriptor pfd = new ParcelFileDescriptor(memoryFile.getFileDescriptor());
-
-        mInstallationSession.setAshmem(pfd, memoryFile.length());
-
-        initPartitionProgress(partitionName, partitionSize, /* readonly = */ true);
-        publishProgress(/* installedSize = */ 0L);
-
         long prevInstalledSize = 0;
-        long installedSize = 0;
-        byte[] bytes = new byte[memoryFile.length()];
-        ExecutorService executor = Executors.newSingleThreadExecutor();
-        Future<Boolean> submitPromise = null;
+        try (SharedMemory sharedMemory =
+                        SharedMemory.create("dsu_buffer_" + partitionName, mSharedMemorySize);
+                MappedMemoryBuffer mappedBuffer =
+                        new MappedMemoryBuffer(sharedMemory.mapReadWrite())) {
+            mInstallationSession.setAshmem(sharedMemory.getFdDup(), sharedMemory.getSize());
 
-        while (true) {
-            final int numBytesRead = sis.read(bytes, 0, bytes.length);
+            initPartitionProgress(partitionName, partitionSize, /* readonly = */ true);
+            publishProgress(/* installedSize = */ 0L);
 
-            if (submitPromise != null) {
-                // Wait until the previous submit task is complete.
-                while (true) {
-                    try {
-                        if (!submitPromise.get()) {
-                            throw new IOException("Failed submitFromAshmem() to DynamicSystem");
+            long installedSize = 0;
+            byte[] readBuffer = new byte[sharedMemory.getSize()];
+            ByteBuffer buffer = mappedBuffer.mBuffer;
+            ExecutorService executor = Executors.newSingleThreadExecutor();
+            Future<Boolean> submitPromise = null;
+
+            while (true) {
+                final int numBytesRead = sis.read(readBuffer, 0, readBuffer.length);
+
+                if (submitPromise != null) {
+                    // Wait until the previous submit task is complete.
+                    while (true) {
+                        try {
+                            if (!submitPromise.get()) {
+                                throw new IOException("Failed submitFromAshmem() to DynamicSystem");
+                            }
+                            break;
+                        } catch (InterruptedException e) {
+                            // Ignore.
                         }
-                        break;
-                    } catch (InterruptedException e) {
-                        // Ignore.
+                    }
+
+                    // Publish the progress of the previous submit task.
+                    if (installedSize > prevInstalledSize + MIN_PROGRESS_TO_PUBLISH) {
+                        publishProgress(installedSize);
+                        prevInstalledSize = installedSize;
                     }
                 }
 
-                // Publish the progress of the previous submit task.
-                if (installedSize > prevInstalledSize + MIN_PROGRESS_TO_PUBLISH) {
-                    publishProgress(installedSize);
-                    prevInstalledSize = installedSize;
+                // Ensure the previous submit task (submitPromise) is complete before exiting the
+                // loop.
+                if (numBytesRead < 0) {
+                    break;
                 }
+
+                if (isCancelled()) {
+                    return;
+                }
+
+                buffer.position(0);
+                buffer.put(readBuffer, 0, numBytesRead);
+                submitPromise =
+                        executor.submit(() -> mInstallationSession.submitFromAshmem(numBytesRead));
+
+                // Even though we update the bytes counter here, the actual progress is updated only
+                // after the submit task (submitPromise) is complete.
+                installedSize += numBytesRead;
             }
-
-            // Ensure the previous submit task (submitPromise) is complete before exiting the loop.
-            if (numBytesRead < 0) {
-                break;
-            }
-
-            if (isCancelled()) {
-                return;
-            }
-
-            memoryFile.writeBytes(bytes, 0, 0, numBytesRead);
-            submitPromise =
-                    executor.submit(() -> mInstallationSession.submitFromAshmem(numBytesRead));
-
-            // Even though we update the bytes counter here, the actual progress is updated only
-            // after the submit task (submitPromise) is complete.
-            installedSize += numBytesRead;
+        } catch (ErrnoException e) {
+            e.rethrowAsIOException();
         }
 
         AvbPublicKey avbPublicKey = new AvbPublicKey();
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
index eec73ff..72383fe 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
@@ -122,15 +122,66 @@
         }
 
         if (activity instanceof AppCompatActivity) {
-            initSupportToolbar((AppCompatActivity) activity);
+            initSettingsStyleToolBar((SupportActionBarHost)
+                    toolBar -> {
+                        AppCompatActivity appCompatActivity = (AppCompatActivity) activity;
+                        appCompatActivity.setSupportActionBar(toolBar);
+                        return appCompatActivity.getSupportActionBar();
+                    });
+        } else {
+            initSettingsStyleToolBar((ActionBarHost)
+                    toolBar -> {
+                        activity.setActionBar(toolBar);
+                        return activity.getActionBar();
+                    });
+        }
+    }
+
+    /**
+     * Initialize some attributes of {@link ActionBar}.
+     *
+     * @param actionBarHost Host Activity that is not AppCompat.
+     */
+    public void initSettingsStyleToolBar(ActionBarHost actionBarHost) {
+        if (actionBarHost == null) {
+            Log.w(TAG, "initSettingsStyleToolBar: actionBarHost is null");
             return;
         }
 
         final Toolbar toolbar = findViewById(R.id.action_bar);
-        activity.setActionBar(toolbar);
+        final ActionBar actionBar = actionBarHost.setupActionBar(toolbar);
 
         // Enable title and home button by default
-        final ActionBar actionBar = activity.getActionBar();
+        if (actionBar != null) {
+            actionBar.setDisplayHomeAsUpEnabled(true);
+            actionBar.setHomeButtonEnabled(true);
+            actionBar.setDisplayShowTitleEnabled(true);
+        }
+    }
+
+    /**
+     * Initialize some attributes of {@link ActionBar}.
+     *
+     * @param supportActionBarHost Host Activity that is AppCompat.
+     */
+    public void initSettingsStyleToolBar(SupportActionBarHost supportActionBarHost) {
+        if (supportActionBarHost == null) {
+            Log.w(TAG, "initSettingsStyleToolBar: supportActionBarHost is null");
+            return;
+        }
+        if (mCollapsingToolbarLayout == null) {
+            return;
+        }
+
+        mCollapsingToolbarLayout.removeAllViews();
+        inflate(getContext(), R.layout.support_toolbar, mCollapsingToolbarLayout);
+        final androidx.appcompat.widget.Toolbar supportToolbar =
+                mCollapsingToolbarLayout.findViewById(R.id.support_action_bar);
+
+        final androidx.appcompat.app.ActionBar actionBar =
+                supportActionBarHost.setupSupportActionBar(supportToolbar);
+
+        // Enable title and home button by default
         if (actionBar != null) {
             actionBar.setDisplayHomeAsUpEnabled(true);
             actionBar.setHomeButtonEnabled(true);
@@ -156,20 +207,27 @@
         }
     }
 
-    /**
-     * Returns an instance of collapsing toolbar.
-     */
+    /** Returns an instance of collapsing toolbar. */
     public CollapsingToolbarLayout getCollapsingToolbarLayout() {
         return mCollapsingToolbarLayout;
     }
 
-    /**
-     * Return an instance of app bar.
-     */
+    /** Return an instance of app bar. */
     public AppBarLayout getAppBarLayout() {
         return mAppBarLayout;
     }
 
+    /** Returns the content frame layout. */
+    public View getContentFrameLayout() {
+        return findViewById(R.id.content_frame);
+    }
+
+    /** Returns the AppCompat Toolbar. */
+    public androidx.appcompat.widget.Toolbar getSupportToolbar() {
+        return (androidx.appcompat.widget.Toolbar)
+            mCollapsingToolbarLayout.findViewById(R.id.support_action_bar);
+    }
+
     private void disableCollapsingToolbarLayoutScrollingBehavior() {
         if (mAppBarLayout == null) {
             return;
@@ -187,25 +245,22 @@
         params.setBehavior(behavior);
     }
 
-    // This API is for supportActionBar of {@link AppCompatActivity}
-    private void initSupportToolbar(AppCompatActivity appCompatActivity) {
-        if (mCollapsingToolbarLayout == null) {
-            return;
-        }
+    /** Interface to be implemented by a host Activity that is not AppCompat. */
+    public interface ActionBarHost {
+        /**
+         * Sets a Toolbar as an actionBar and optionally returns an ActionBar represented by
+         * this toolbar if it should be used.
+         */
+        @Nullable ActionBar setupActionBar(Toolbar toolbar);
+    }
 
-        mCollapsingToolbarLayout.removeAllViews();
-        inflate(getContext(), R.layout.support_toolbar, mCollapsingToolbarLayout);
-        final androidx.appcompat.widget.Toolbar supportToolbar =
-                mCollapsingToolbarLayout.findViewById(R.id.support_action_bar);
-
-        appCompatActivity.setSupportActionBar(supportToolbar);
-
-        // Enable title and home button by default
-        final androidx.appcompat.app.ActionBar actionBar = appCompatActivity.getSupportActionBar();
-        if (actionBar != null) {
-            actionBar.setDisplayHomeAsUpEnabled(true);
-            actionBar.setHomeButtonEnabled(true);
-            actionBar.setDisplayShowTitleEnabled(true);
-        }
+    /** Interface to be implemented by a host Activity that is AppCompat. */
+    public interface SupportActionBarHost {
+        /**
+         * Sets a Toolbar as an actionBar and optionally returns an ActionBar represented by
+         * this toolbar if it should be used.
+         */
+        @Nullable androidx.appcompat.app.ActionBar setupSupportActionBar(
+                androidx.appcompat.widget.Toolbar toolbar);
     }
 }
diff --git a/packages/SettingsLib/SettingsSpinner/res/layout-v31/settings_spinner_dropdown_view.xml b/packages/SettingsLib/SettingsSpinner/res/layout-v33/settings_spinner_dropdown_view.xml
similarity index 100%
rename from packages/SettingsLib/SettingsSpinner/res/layout-v31/settings_spinner_dropdown_view.xml
rename to packages/SettingsLib/SettingsSpinner/res/layout-v33/settings_spinner_dropdown_view.xml
diff --git a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_view.xml b/packages/SettingsLib/SettingsSpinner/res/layout-v33/settings_spinner_view.xml
similarity index 93%
rename from packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_view.xml
rename to packages/SettingsLib/SettingsSpinner/res/layout-v33/settings_spinner_view.xml
index 75de34e..1d0c9b9 100644
--- a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_view.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/layout-v33/settings_spinner_view.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     Copyright (C) 2018 The Android Open Source Project
+     Copyright (C) 2022 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
diff --git a/packages/SettingsLib/SettingsSpinner/res/values/dimens.xml b/packages/SettingsLib/SettingsSpinner/res/values-v33/dimens.xml
similarity index 93%
rename from packages/SettingsLib/SettingsSpinner/res/values/dimens.xml
rename to packages/SettingsLib/SettingsSpinner/res/values-v33/dimens.xml
index d526df6..10aa848 100644
--- a/packages/SettingsLib/SettingsSpinner/res/values/dimens.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/values-v33/dimens.xml
@@ -16,5 +16,6 @@
 
 <resources>
     <dimen name="spinner_height">36dp</dimen>
+    <dimen name="spinner_dropdown_height">48dp</dimen>
     <dimen name="spinner_padding_top_or_bottom">8dp</dimen>
 </resources>
diff --git a/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml b/packages/SettingsLib/SettingsSpinner/res/values-v33/styles.xml
similarity index 96%
rename from packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml
rename to packages/SettingsLib/SettingsSpinner/res/values-v33/styles.xml
index fd45a16..6e26ae1 100644
--- a/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/values-v33/styles.xml
@@ -35,7 +35,7 @@
         <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item>
         <item name="android:maxLines">1</item>
         <item name="android:ellipsize">marquee</item>
-        <item name="android:minHeight">@dimen/spinner_height</item>
+        <item name="android:minHeight">@dimen/spinner_dropdown_height</item>
         <item name="android:paddingStart">16dp</item>
         <item name="android:paddingEnd">36dp</item>
         <item name="android:paddingTop">@dimen/spinner_padding_top_or_bottom</item>
diff --git a/packages/SettingsLib/SettingsSpinner/res/values/styles.xml b/packages/SettingsLib/SettingsSpinner/res/values/styles.xml
deleted file mode 100644
index 8ea1f9a..0000000
--- a/packages/SettingsLib/SettingsSpinner/res/values/styles.xml
+++ /dev/null
@@ -1,28 +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.
-  -->
-
-<resources>
-    <style name="SettingsSpinnerTitleBar">
-        <item name="android:textAppearance">?android:attr/textAppearanceButton</item>
-        <item name="android:maxLines">1</item>
-        <item name="android:ellipsize">marquee</item>
-        <item name="android:paddingStart">16dp</item>
-        <item name="android:paddingEnd">36dp</item>
-        <item name="android:paddingTop">@dimen/spinner_padding_top_or_bottom</item>
-        <item name="android:paddingBottom">@dimen/spinner_padding_top_or_bottom</item>
-    </style>
-</resources>
diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java
index 2611207..7288494 100644
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java
+++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java
@@ -41,7 +41,7 @@
      *                access the current theme, resources, etc.
      */
     public SettingsSpinnerAdapter(Context context) {
-        super(context, DEFAULT_RESOURCE);
+        super(context, getDefaultResource());
 
         setDropDownViewResource(getDropdownResource());
         mDefaultInflater = LayoutInflater.from(context);
@@ -51,7 +51,7 @@
      * In overridded {@link #getView(int, View, ViewGroup)}, use this method to get default view.
      */
     public View getDefaultView(int position, View convertView, ViewGroup parent) {
-        return mDefaultInflater.inflate(DEFAULT_RESOURCE, parent, false /* attachToRoot */);
+        return mDefaultInflater.inflate(getDefaultResource(), parent, false /* attachToRoot */);
     }
 
     /**
@@ -62,8 +62,12 @@
         return mDefaultInflater.inflate(getDropdownResource(), parent, false /* attachToRoot */);
     }
 
-    private int getDropdownResource() {
-        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
+    private static int getDefaultResource() {
+        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+                ? DEFAULT_RESOURCE : android.R.layout.simple_spinner_dropdown_item;
+    }
+    private static int getDropdownResource() {
+        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
                 ? DFAULT_DROPDOWN_RESOURCE : android.R.layout.simple_spinner_dropdown_item;
     }
 }
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_background.xml
deleted file mode 100644
index e1764af..0000000
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_background.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2022 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<ripple
-xmlns:android="http://schemas.android.com/apk/res/android"
-android:color="@color/settingslib_ripple_color">
-
-<item android:id="@android:id/background">
-    <layer-list android:paddingMode="stack">
-        <item
-            android:top="8dp"
-            android:bottom="8dp">
-
-            <shape>
-                <corners android:radius="28dp"/>
-                <solid android:color="@android:color/system_accent1_100"/>
-                <size android:height="@dimen/settingslib_spinner_height"/>
-            </shape>
-        </item>
-
-        <item
-            android:gravity="center|end"
-            android:width="18dp"
-            android:height="18dp"
-            android:end="12dp"
-            android:drawable="@drawable/settingslib_arrow_drop_down"/>
-    </layer-list>
-</item>
-</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_arrow_drop_down.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_arrow_drop_down.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_arrow_drop_down.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_arrow_drop_down.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_spinner_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_spinner_background.xml
similarity index 62%
rename from packages/SettingsLib/SettingsTheme/res/drawable/settingslib_spinner_background.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_spinner_background.xml
index 7466712..fbda832 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_spinner_background.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_spinner_background.xml
@@ -16,33 +16,30 @@
   -->
 
 <ripple
-xmlns:android="http://schemas.android.com/apk/res/android"
-android:color="@color/settingslib_ripple_color">
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="@color/settingslib_ripple_color">
 
     <item android:id="@android:id/background">
-        <layer-list android:paddingMode="stack">
+        <layer-list
+            android:paddingMode="stack"
+            android:paddingStart="0dp"
+            android:paddingEnd="24dp">
             <item
                 android:top="8dp"
                 android:bottom="8dp">
 
                 <shape>
-                    <corners
-                        android:radius="20dp"/>
-                    <solid
-                        android:color="?android:attr/colorPrimary"/>
-                    <stroke
-                        android:color="#1f000000"
-                        android:width="1dp"/>
-                    <size
-                        android:height="32dp"/>
+                    <corners android:radius="28dp"/>
+                    <solid android:color="@android:color/system_accent1_100"/>
+                    <size android:height="@dimen/settingslib_spinner_height"/>
                 </shape>
             </item>
 
             <item
                 android:gravity="center|end"
-                android:width="24dp"
-                android:height="24dp"
-                android:end="4dp"
+                android:width="18dp"
+                android:height="18dp"
+                android:end="12dp"
                 android:drawable="@drawable/settingslib_arrow_drop_down"/>
         </layer-list>
     </item>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_dropdown_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_spinner_dropdown_background.xml
similarity index 68%
rename from packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_dropdown_background.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_spinner_dropdown_background.xml
index 056fb82..50ef8fb 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_dropdown_background.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_spinner_dropdown_background.xml
@@ -20,9 +20,17 @@
     android:color="@color/settingslib_ripple_color">
 
     <item android:id="@android:id/background">
-        <shape>
-            <corners android:radius="10dp"/>
-            <solid android:color="@android:color/system_accent2_100"/>
-        </shape>
+        <layer-list
+            android:paddingMode="stack"
+            android:paddingStart="0dp"
+            android:paddingEnd="12dp">
+
+            <item>
+                <shape>
+                    <corners android:radius="10dp"/>
+                    <solid android:color="@android:color/system_accent2_100"/>
+                </shape>
+            </item>
+        </layer-list>
     </item>
 </ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
index 29fdab1..11546c8 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
@@ -25,6 +25,4 @@
     <dimen name="settingslib_listPreferredItemPaddingStart">24dp</dimen>
     <!-- Right padding of the preference -->
     <dimen name="settingslib_listPreferredItemPaddingEnd">24dp</dimen>
-
-    <dimen name="settingslib_spinner_height">36dp</dimen>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
index b12c6d2..3597ee9 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
@@ -45,24 +45,4 @@
         <item name="android:progressDrawable">@drawable/settingslib_progress_horizontal</item>
         <item name="android:scaleY">0.5</item>
     </style>
-
-    <style name="Spinner.SettingsLib"
-           parent="android:style/Widget.Material.Spinner">
-        <item name="android:background">@drawable/settingslib_spinner_background</item>
-        <item name="android:popupBackground">@drawable/settingslib_spinner_dropdown_background</item>
-        <item name="android:dropDownVerticalOffset">48dp</item>
-        <item name="android:layout_marginTop">16dp</item>
-        <item name="android:layout_marginBottom">8dp</item>
-    </style>
-
-    <style name="SpinnerItem.SettingsLib"
-           parent="@android:style/Widget.DeviceDefault.TextView.SpinnerItem">
-        <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item>
-    </style>
-
-    <style name="SpinnerDropDownItem.SettingsLib"
-           parent="@android:style/Widget.Material.DropDownItem.Spinner">
-        <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item>
-    </style>
-
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
index 4f426a3..69c122c 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
@@ -17,7 +17,7 @@
 
 <resources>
     <!-- Only using in Settings application -->
-    <style name="Theme.SettingsBase" parent="@android:style/Theme.DeviceDefault.Settings" >
+    <style name="Theme.SettingsBase_v31" parent="@android:style/Theme.DeviceDefault.Settings" >
         <item name="android:textAppearanceListItem">@style/TextAppearance.PreferenceTitle.SettingsLib</item>
         <item name="android:listPreferredItemPaddingStart">@dimen/settingslib_listPreferredItemPaddingStart</item>
         <item name="android:listPreferredItemPaddingLeft">@dimen/settingslib_listPreferredItemPaddingStart</item>
@@ -26,11 +26,10 @@
         <item name="preferenceTheme">@style/PreferenceTheme.SettingsLib</item>
         <item name="android:switchStyle">@style/Switch.SettingsLib</item>
         <item name="android:progressBarStyleHorizontal">@style/HorizontalProgressBar.SettingsLib</item>
-        <item name="android:spinnerStyle">@style/Spinner.SettingsLib</item>
-        <item name="android:spinnerItemStyle">@style/SpinnerItem.SettingsLib</item>
-        <item name="android:spinnerDropDownItemStyle">@style/SpinnerDropDownItem.SettingsLib</item>
     </style>
 
+    <style name="Theme.SettingsBase" parent="Theme.SettingsBase_v31" />
+
     <!-- Using in SubSettings page including injected settings page -->
     <style name="Theme.SubSettingsBase" parent="Theme.SettingsBase">
         <!-- Suppress the built-in action bar -->
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_arrow_drop_down.xml b/packages/SettingsLib/SettingsTheme/res/values-v33/dimens.xml
similarity index 66%
rename from packages/SettingsLib/SettingsTheme/res/drawable/settingslib_arrow_drop_down.xml
rename to packages/SettingsLib/SettingsTheme/res/values-v33/dimens.xml
index 6ed215d..bec807b 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_arrow_drop_down.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v33/dimens.xml
@@ -15,12 +15,6 @@
   limitations under the License.
   -->
 
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:viewportWidth="24"
-        android:viewportHeight="24"
-        android:width="24dp"
-        android:height="24dp">
-    <path
-        android:pathData="M7 10l5 5 5 -5z"
-        android:fillColor="?android:attr/textColorPrimary"/>
-</vector>
+<resources>
+    <dimen name="settingslib_spinner_height">36dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v33/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v33/styles.xml
new file mode 100644
index 0000000..15fdfe4
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values-v33/styles.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<resources>
+    <style name="Spinner.SettingsLib"
+           parent="android:style/Widget.Material.Spinner">
+        <item name="android:background">@drawable/settingslib_spinner_background</item>
+        <item name="android:popupBackground">@drawable/settingslib_spinner_dropdown_background
+        </item>
+        <item name="android:dropDownVerticalOffset">48dp</item>
+        <item name="android:layout_marginTop">16dp</item>
+        <item name="android:layout_marginBottom">8dp</item>
+    </style>
+
+    <style name="SpinnerItem.SettingsLib"
+           parent="@android:style/Widget.DeviceDefault.TextView.SpinnerItem">
+        <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item>
+        <item name="android:paddingStart">16dp</item>
+    </style>
+
+    <style name="SpinnerDropDownItem.SettingsLib"
+           parent="@android:style/Widget.Material.DropDownItem.Spinner">
+        <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item>
+        <item name="android:paddingStart">16dp</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v33/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v33/themes.xml
new file mode 100644
index 0000000..24e3c46
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values-v33/themes.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2022 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+  -->
+
+<resources>
+    <style name="Theme.SettingsBase" parent="Theme.SettingsBase_v31" >
+        <item name="android:spinnerStyle">@style/Spinner.SettingsLib</item>
+        <item name="android:spinnerItemStyle">@style/SpinnerItem.SettingsLib</item>
+        <item name="android:spinnerDropDownItemStyle">@style/SpinnerDropDownItem.SettingsLib</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles.xml b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
index fa27bb6..aaab0f0 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
@@ -26,10 +26,4 @@
     <style name="TextAppearance.CategoryTitle.SettingsLib"
            parent="@android:style/TextAppearance.DeviceDefault.Medium">
     </style>
-
-    <style name="Spinner.SettingsLib"
-           parent="android:style/Widget.Material.Spinner">
-        <item name="android:background">@drawable/settingslib_spinner_background</item>
-        <item name="android:dropDownVerticalOffset">48dp</item>
-    </style>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values/themes.xml b/packages/SettingsLib/SettingsTheme/res/values/themes.xml
index 8dc0f3c..2d881d1 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/themes.xml
@@ -19,7 +19,6 @@
     <!-- Only using in Settings application -->
     <style name="Theme.SettingsBase" parent="@android:style/Theme.DeviceDefault.Settings">
         <item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
-        <item name="android:spinnerStyle">@style/Spinner.SettingsLib</item>
     </style>
 
     <!-- Using in SubSettings page including injected settings page -->
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index eb7fea3..6887d03 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -224,6 +224,7 @@
     <uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS" />
     <uses-permission android:name="android.permission.GET_RUNTIME_PERMISSIONS" />
     <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
+    <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" />
 
     <!-- It's like, reality, but, you know, virtual -->
     <uses-permission android:name="android.permission.ACCESS_VR_MANAGER" />
@@ -522,7 +523,8 @@
                   android:launchMode="singleTop"
                   android:permission="android.permission.MANAGE_SENSOR_PRIVACY"
                   android:theme="@style/Theme.SystemUI.Dialog.Alert"
-                  android:finishOnCloseSystemDialogs="true">
+                  android:finishOnCloseSystemDialogs="true"
+                  android:showForAllUsers="true">
         </activity>
 
         <!-- started from SensoryPrivacyService -->
@@ -531,7 +533,8 @@
                   android:launchMode="singleTop"
                   android:permission="android.permission.MANAGE_SENSOR_PRIVACY"
                   android:theme="@style/BottomSheet"
-                  android:finishOnCloseSystemDialogs="true">
+                  android:finishOnCloseSystemDialogs="true"
+                  android:showForAllUsers="true">
         </activity>
 
 
diff --git a/packages/SystemUI/res/anim/media_metadata_enter.xml b/packages/SystemUI/res/anim/media_metadata_enter.xml
new file mode 100644
index 0000000..fccb766
--- /dev/null
+++ b/packages/SystemUI/res/anim/media_metadata_enter.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:ordering="together">
+    <objectAnimator
+        android:propertyName="translationX"
+        android:valueFrom="32dp"
+        android:valueTo="0dp"
+        android:duration="600"
+        android:valueType="floatType" />
+    <objectAnimator
+        android:propertyName="transitionAlpha"
+        android:valueFrom="0"
+        android:valueTo="1"
+        android:duration="167"
+        android:valueType="floatType" />
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/media_metadata_exit.xml b/packages/SystemUI/res/anim/media_metadata_exit.xml
new file mode 100644
index 0000000..0ee1171
--- /dev/null
+++ b/packages/SystemUI/res/anim/media_metadata_exit.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:ordering="together">
+    <objectAnimator
+        android:propertyName="translationX"
+        android:valueFrom="0dp"
+        android:valueTo="-32dp"
+        android:duration="167"
+        android:valueType="floatType" />
+    <objectAnimator
+        android:propertyName="transitionAlpha"
+        android:valueFrom="1"
+        android:valueTo="0"
+        android:duration="83"
+        android:startOffset="83"
+        android:valueType="floatType" />
+</set>
diff --git a/packages/SystemUI/res/drawable/overlay_button_background.xml b/packages/SystemUI/res/drawable/overlay_button_background.xml
index 0e8438c..c045048 100644
--- a/packages/SystemUI/res/drawable/overlay_button_background.xml
+++ b/packages/SystemUI/res/drawable/overlay_button_background.xml
@@ -18,7 +18,7 @@
      (clipboard text editor, long screenshots) -->
 <ripple xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-        android:color="?android:textColorPrimary">
+        android:color="@color/overlay_button_ripple">
     <item android:id="@android:id/background">
         <inset android:insetTop="4dp" android:insetBottom="4dp">
             <shape android:shape="rectangle">
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 7e8112a..4f7d099 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -135,6 +135,15 @@
             android:layout_height="wrap_content"
             style="@*android:style/TextAppearance.DeviceDefault.Notification" />
 
+        <!-- Non configurable app/channel text. appears instead of @+id/interruptiveness_settings-->
+        <TextView
+            android:id="@+id/non_configurable_call_text"
+            android:text="@string/notification_unblockable_call_desc"
+            android:visibility="gone"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="@*android:style/TextAppearance.DeviceDefault.Notification" />
+
         <!-- Non configurable multichannel text. appears instead of @+id/interruptiveness_settings-->
         <TextView
             android:id="@+id/non_configurable_multichannel_text"
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 2c29f07..2fb6d6c 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -55,7 +55,7 @@
             android:clipChildren="false"
             android:clipToPadding="false"
             android:focusable="true"
-            android:paddingBottom="24dp"
+            android:paddingBottom="@dimen/qqs_layout_padding_bottom"
             android:importantForAccessibility="yes">
         </com.android.systemui.qs.QuickQSPanel>
     </RelativeLayout>
diff --git a/packages/SystemUI/res/layout/rounded_corners_bottom.xml b/packages/SystemUI/res/layout/rounded_corners_bottom.xml
deleted file mode 100644
index bb6d4bd..0000000
--- a/packages/SystemUI/res/layout/rounded_corners_bottom.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-** Copyright 2020, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
--->
-<com.android.systemui.RegionInterceptingFrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/rounded_corners_bottom"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-    <ImageView
-        android:id="@+id/left"
-        android:layout_width="12dp"
-        android:layout_height="12dp"
-        android:layout_gravity="left|bottom"
-        android:tint="#ff000000"
-        android:visibility="gone"
-        android:src="@drawable/rounded_corner_bottom"/>
-
-    <ImageView
-        android:id="@+id/right"
-        android:layout_width="12dp"
-        android:layout_height="12dp"
-        android:tint="#ff000000"
-        android:visibility="gone"
-        android:layout_gravity="right|bottom"
-        android:src="@drawable/rounded_corner_bottom"/>
-
-</com.android.systemui.RegionInterceptingFrameLayout>
diff --git a/packages/SystemUI/res/layout/rounded_corners_top.xml b/packages/SystemUI/res/layout/rounded_corners_top.xml
deleted file mode 100644
index 46648c8..0000000
--- a/packages/SystemUI/res/layout/rounded_corners_top.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-** Copyright 2020, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
--->
-<com.android.systemui.RegionInterceptingFrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/rounded_corners_top"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-    <ImageView
-        android:id="@+id/left"
-        android:layout_width="12dp"
-        android:layout_height="12dp"
-        android:layout_gravity="left|top"
-        android:tint="#ff000000"
-        android:visibility="gone"
-        android:src="@drawable/rounded_corner_top"/>
-
-    <ImageView
-        android:id="@+id/right"
-        android:layout_width="12dp"
-        android:layout_height="12dp"
-        android:tint="#ff000000"
-        android:visibility="gone"
-        android:layout_gravity="right|top"
-        android:src="@drawable/rounded_corner_top"/>
-
-</com.android.systemui.RegionInterceptingFrameLayout>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 3a638b1..dc2bee5 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -59,7 +59,6 @@
     <color name="global_actions_alert_text">@color/GM2_red_300</color>
 
     <!-- Floating overlay actions -->
-    <color name="overlay_button_ripple">#42FFFFFF</color>
     <color name="overlay_background_protection_start">#80000000</color> <!-- 50% black -->
 
     <!-- Media -->
diff --git a/packages/SystemUI/res/values-sw600dp-port/dimens.xml b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
index f5c0509..589d12f 100644
--- a/packages/SystemUI/res/values-sw600dp-port/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
@@ -20,4 +20,6 @@
     <dimen name="keyguard_clock_top_margin">40dp</dimen>
     <dimen name="keyguard_status_view_bottom_margin">40dp</dimen>
     <dimen name="bouncer_user_switcher_y_trans">20dp</dimen>
+
+    <dimen name="qqs_layout_padding_bottom">16dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-sw720dp-port/dimens.xml b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
index 44f8f3a..fc12d41 100644
--- a/packages/SystemUI/res/values-sw720dp-port/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
@@ -25,6 +25,8 @@
     <dimen name="keyguard_status_view_bottom_margin">80dp</dimen>
     <dimen name="bouncer_user_switcher_y_trans">90dp</dimen>
 
+    <dimen name="qqs_layout_padding_bottom">40dp</dimen>
+
     <dimen name="notification_panel_margin_horizontal">96dp</dimen>
     <dimen name="notification_side_paddings">40dp</dimen>
     <dimen name="notification_section_divider_height">16dp</dimen>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ff3cb5f..d148403 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -474,6 +474,7 @@
     <dimen name="qs_brightness_margin_top">8dp</dimen>
     <dimen name="qs_brightness_margin_bottom">24dp</dimen>
     <dimen name="qqs_layout_margin_top">16dp</dimen>
+    <dimen name="qqs_layout_padding_bottom">24dp</dimen>
 
     <dimen name="qs_customize_internal_side_paddings">8dp</dimen>
     <dimen name="qs_icon_size">20dp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index ff71b4f..5eacc3e 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -170,5 +170,11 @@
     <item type="id" name="action_move_bottom_right"/>
     <item type="id" name="action_move_to_edge_and_hide"/>
     <item type="id" name="action_move_out_edge_and_show"/>
+
+    <!-- rounded corner view id -->
+    <item type="id" name="rounded_corner_top_left"/>
+    <item type="id" name="rounded_corner_top_right"/>
+    <item type="id" name="rounded_corner_bottom_left"/>
+    <item type="id" name="rounded_corner_bottom_right"/>
 </resources>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b248efe..0f5115b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1373,6 +1373,9 @@
     <!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. -->
     <string name="notification_unblockable_desc">These notifications can\'t be modified.</string>
 
+    <!-- Notification: Control panel: Label that displays when a notification cannot be blocked because it's attached to a phone/voip call. -->
+    <string name="notification_unblockable_call_desc">Call notifications can\'t be modified.</string>
+
     <!-- Notification: Control panel: label that displays when viewing settings for a group of notifications posted to multiple channels. -->
     <string name="notification_multichannel_desc">This group of notifications cannot be configured here</string>
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
index 238690c..938b1ca 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
@@ -80,21 +80,29 @@
 
     public PictureInPictureSurfaceTransaction scaleAndCrop(
             SurfaceControl.Transaction tx, SurfaceControl leash,
-            Rect sourceBounds, Rect destinationBounds, Rect insets) {
+            Rect sourceRectHint, Rect sourceBounds, Rect destinationBounds, Rect insets) {
         mTmpSourceRectF.set(sourceBounds);
         mTmpDestinationRect.set(sourceBounds);
         mTmpDestinationRect.inset(insets);
         // Scale by the shortest edge and offset such that the top/left of the scaled inset
         // source rect aligns with the top/left of the destination bounds
-        final float scale = sourceBounds.width() <= sourceBounds.height()
-                ? (float) destinationBounds.width() / sourceBounds.width()
-                : (float) destinationBounds.height() / sourceBounds.height();
+        final float scale;
+        if (sourceRectHint.isEmpty() || sourceRectHint.width() == sourceBounds.width()) {
+            scale = sourceBounds.width() <= sourceBounds.height()
+                    ? (float) destinationBounds.width() / sourceBounds.width()
+                    : (float) destinationBounds.height() / sourceBounds.height();
+        } else {
+            // scale by sourceRectHint if it's not edge-to-edge
+            scale = sourceRectHint.width() <= sourceRectHint.height()
+                    ? (float) destinationBounds.width() / sourceRectHint.width()
+                    : (float) destinationBounds.height() / sourceRectHint.height();
+        }
         final float left = destinationBounds.left - (insets.left + sourceBounds.left) * scale;
         final float top = destinationBounds.top - (insets.top + sourceBounds.top) * scale;
         mTmpTransform.setScale(scale, scale);
         final float cornerRadius = getScaledCornerRadius(mTmpDestinationRect, destinationBounds);
         tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
-                .setWindowCrop(leash, mTmpDestinationRect)
+                .setCrop(leash, mTmpDestinationRect)
                 .setPosition(leash, left, top)
                 .setCornerRadius(leash, cornerRadius)
                 .setShadowRadius(leash, mShadowRadius);
@@ -127,7 +135,7 @@
             adjustedPositionY = positionY - insets.left * scale;
         }
         tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
-                .setWindowCrop(leash, mTmpDestinationRect)
+                .setCrop(leash, mTmpDestinationRect)
                 .setPosition(leash, adjustedPositionX, adjustedPositionY)
                 .setCornerRadius(leash, cornerRadius)
                 .setShadowRadius(leash, mShadowRadius);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 32299f5..5bd81a4 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -86,6 +86,7 @@
     public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = InsetsState.ITYPE_RIGHT_TAPPABLE_ELEMENT;
     public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT =
             InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
+    public static final int ITYPE_SIZE = InsetsState.SIZE;
 
     public static final int ANIMATION_DURATION_RESIZE = InsetsController.ANIMATION_DURATION_RESIZE;
     public static final Interpolator RESIZE_INTERPOLATOR = InsetsController.RESIZE_INTERPOLATOR;
diff --git a/packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt b/packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt
index 1b2ea3b..a08c900 100644
--- a/packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt
+++ b/packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt
@@ -23,7 +23,7 @@
      *  Scale the alpha/position of the host view.
      */
     @JvmStatic
-    fun getHostViewScaledExpansion(fraction: Float): Float {
+    fun showBouncerProgress(fraction: Float): Float {
         return when {
                     fraction >= 0.9f -> 1f
                     fraction < 0.6 -> 0f
@@ -35,7 +35,7 @@
      *  Scale the alpha/tint of the back scrim.
      */
     @JvmStatic
-    fun getBackScrimScaledExpansion(fraction: Float): Float {
+    fun aboutToShowBouncerProgress(fraction: Float): Float {
         return MathUtils.constrain((fraction - 0.9f) / 0.1f, 0f, 1f)
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index b3cf927..04c9a45 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -130,6 +130,24 @@
         }
     };
 
+    private final KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener
+            mKeyguardUnlockAnimationListener =
+            new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() {
+                @Override
+                public void onSmartspaceSharedElementTransitionStarted() {
+                    // The smartspace needs to be able to translate out of bounds in order to
+                    // end up where the launcher's smartspace is, while its container is being
+                    // swiped off the top of the screen.
+                    setClipChildrenForUnlock(false);
+                }
+
+                @Override
+                public void onUnlockAnimationFinished() {
+                    // For performance reasons, reset this once the unlock animation ends.
+                    setClipChildrenForUnlock(true);
+                }
+            };
+
     @Inject
     public KeyguardClockSwitchController(
             KeyguardClockSwitch keyguardClockSwitch,
@@ -162,22 +180,6 @@
         mUiExecutor = uiExecutor;
         mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
         mDumpManager = dumpManager;
-        mKeyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
-                new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() {
-                    @Override
-                    public void onSmartspaceSharedElementTransitionStarted() {
-                        // The smartspace needs to be able to translate out of bounds in order to
-                        // end up where the launcher's smartspace is, while its container is being
-                        // swiped off the top of the screen.
-                        setClipChildrenForUnlock(false);
-                    }
-
-                    @Override
-                    public void onUnlockAnimationFinished() {
-                        // For performance reasons, reset this once the unlock animation ends.
-                        setClipChildrenForUnlock(true);
-                    }
-                });
     }
 
     /**
@@ -272,6 +274,9 @@
         );
 
         updateDoubleLineClock();
+
+        mKeyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
+                mKeyguardUnlockAnimationListener);
     }
 
     int getNotificationIconAreaHeight() {
@@ -287,6 +292,9 @@
         mView.setClockPlugin(null, mStatusBarStateController.getState());
 
         mSecureSettings.unregisterContentObserver(mDoubleLineClockObserver);
+
+        mKeyguardUnlockAnimationController.removeKeyguardUnlockAnimationListener(
+                mKeyguardUnlockAnimationListener);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index 8c3e066..239b4789 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -327,7 +327,7 @@
      * @param fraction amount of the screen that should show.
      */
     public void setExpansion(float fraction) {
-        float scaledFraction = BouncerPanelExpansionCalculator.getHostViewScaledExpansion(fraction);
+        float scaledFraction = BouncerPanelExpansionCalculator.showBouncerProgress(fraction);
         mView.setAlpha(MathUtils.constrain(1 - scaledFraction, 0f, 1f));
         mView.setTranslationY(scaledFraction * mTranslationY);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index ff5715c..9aa5fae 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -63,7 +63,6 @@
             Key.QS_WORK_ADDED,
             Key.QS_NIGHTDISPLAY_ADDED,
             Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
-            Key.SEEN_MULTI_USER,
             Key.SEEN_RINGER_GUIDANCE_COUNT,
             Key.QS_HAS_TURNED_OFF_MOBILE_DATA,
             Key.TOUCHED_RINGER_TOGGLE,
@@ -106,7 +105,6 @@
          * Settings panel.
          */
         String QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT = "QsLongPressTooltipShownCount";
-        String SEEN_MULTI_USER = "HasSeenMultiUser";
         String SEEN_RINGER_GUIDANCE_COUNT = "RingerGuidanceCount";
         String QS_TILE_SPECS_REVEALED = "QsTileSpecsRevealed";
         String QS_HAS_TURNED_OFF_MOBILE_DATA = "QsHasTurnedOffMobileData";
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 43d91a2..9b09101 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -77,6 +77,7 @@
 import com.android.systemui.decor.DecorProviderKt;
 import com.android.systemui.decor.OverlayWindow;
 import com.android.systemui.decor.PrivacyDotDecorProviderFactory;
+import com.android.systemui.decor.RoundedCornerDecorProviderFactory;
 import com.android.systemui.decor.RoundedCornerResDelegate;
 import com.android.systemui.qs.SettingObserver;
 import com.android.systemui.settings.UserTracker;
@@ -137,6 +138,9 @@
     @VisibleForTesting
     protected RoundedCornerResDelegate mRoundedCornerResDelegate;
     @VisibleForTesting
+    protected DecorProviderFactory mRoundedCornerFactory;
+    private int mProviderRefreshToken = 0;
+    @VisibleForTesting
     protected OverlayWindow[] mOverlays = null;
     @VisibleForTesting
     @Nullable
@@ -282,16 +286,58 @@
         return mDotFactory.getHasProviders();
     }
 
+    @NonNull
+    private List<DecorProvider> getProviders(boolean hasHwLayer) {
+        List<DecorProvider> decorProviders = new ArrayList<>(mDotFactory.getProviders());
+        if (!hasHwLayer) {
+            decorProviders.addAll(mRoundedCornerFactory.getProviders());
+        }
+        return decorProviders;
+    }
+
+    private void updateDisplayIdToProviderFactories() {
+        mDotFactory.onDisplayUniqueIdChanged(mDisplayUniqueId);
+        mRoundedCornerFactory.onDisplayUniqueIdChanged(mDisplayUniqueId);
+    }
+
+    /**
+     * Check that newProviders is the same list with decorProviders inside mOverlay.
+     * @param newProviders expected comparing DecorProviders
+     * @return true if same provider list
+     */
+    @VisibleForTesting
+    boolean hasSameProviders(@NonNull List<DecorProvider> newProviders) {
+        final ArrayList<Integer> overlayViewIds = new ArrayList<>();
+        if (mOverlays != null) {
+            for (OverlayWindow overlay : mOverlays) {
+                if (overlay == null) {
+                    continue;
+                }
+                overlayViewIds.addAll(overlay.getViewIds());
+            }
+        }
+        if (overlayViewIds.size() != newProviders.size()) {
+            return false;
+        }
+
+        for (DecorProvider provider: newProviders) {
+            if (!overlayViewIds.contains(provider.getViewId())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     private void startOnScreenDecorationsThread() {
         mRotation = mContext.getDisplay().getRotation();
         mDisplayUniqueId = mContext.getDisplay().getUniqueId();
         mRoundedCornerResDelegate = new RoundedCornerResDelegate(mContext.getResources(),
                 mDisplayUniqueId);
+        mRoundedCornerFactory = new RoundedCornerDecorProviderFactory(mRoundedCornerResDelegate);
         mWindowManager = mContext.getSystemService(WindowManager.class);
         mDisplayManager = mContext.getSystemService(DisplayManager.class);
         mHwcScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport();
-        updateRoundedCornerDrawable();
-        updateRoundedCornerRadii();
+        updateHwLayerRoundedCornerDrawable();
         setupDecorations();
         setupCameraListener();
 
@@ -343,18 +389,27 @@
                 final String newUniqueId = mContext.getDisplay().getUniqueId();
                 if (!Objects.equals(newUniqueId, mDisplayUniqueId)) {
                     mDisplayUniqueId = newUniqueId;
-                    mRoundedCornerResDelegate.reloadAll(newUniqueId);
                     final DisplayDecorationSupport newScreenDecorationSupport =
                             mContext.getDisplay().getDisplayDecorationSupport();
-                    // When the value of mSupportHwcScreenDecoration is changed, re-setup the whole
-                    // screen decoration.
-                    if (!eq(newScreenDecorationSupport, mHwcScreenDecorationSupport)) {
+
+                    updateDisplayIdToProviderFactories();
+
+                    // When providers or the value of mSupportHwcScreenDecoration is changed,
+                    // re-setup the whole screen decoration.
+                    if (!hasSameProviders(getProviders(newScreenDecorationSupport != null))
+                            || !eq(newScreenDecorationSupport, mHwcScreenDecorationSupport)) {
                         mHwcScreenDecorationSupport = newScreenDecorationSupport;
                         removeAllOverlays();
                         setupDecorations();
                         return;
                     }
-                    updateRoundedCornerDrawable();
+
+                    if (mScreenDecorHwcLayer != null) {
+                        updateHwLayerRoundedCornerDrawable();
+                        updateHwLayerRoundedCornerSize();
+                    }
+
+                    updateOverlayProviderViews();
                 }
                 if (mCutoutViews != null) {
                     final int size = mCutoutViews.length;
@@ -369,7 +424,6 @@
                 if (mScreenDecorHwcLayer != null) {
                     mScreenDecorHwcLayer.onDisplayChanged(displayId);
                 }
-                updateOrientation();
             }
         };
 
@@ -396,6 +450,19 @@
         return null;
     }
 
+    private void removeRedundantOverlayViews(@NonNull List<DecorProvider> decorProviders) {
+        if (mOverlays == null) {
+            return;
+        }
+        int[] viewIds = decorProviders.stream().mapToInt(DecorProvider::getViewId).toArray();
+        for (final OverlayWindow overlay : mOverlays) {
+            if (overlay == null) {
+                continue;
+            }
+            overlay.removeRedundantViews(viewIds);
+        }
+    }
+
     private void removeOverlayView(@IdRes int id) {
         if (mOverlays == null) {
             return;
@@ -411,9 +478,10 @@
     }
 
     private void setupDecorations() {
-        List<DecorProvider> decorProviders = mDotFactory.getProviders();
+        if (hasRoundedCorners() || shouldDrawCutout() || isPrivacyDotEnabled()) {
+            List<DecorProvider> decorProviders = getProviders(mHwcScreenDecorationSupport != null);
+            removeRedundantOverlayViews(decorProviders);
 
-        if (hasRoundedCorners() || shouldDrawCutout() || !decorProviders.isEmpty()) {
             if (mHwcScreenDecorationSupport != null) {
                 createHwcOverlay();
             } else {
@@ -427,7 +495,7 @@
                     Pair<List<DecorProvider>, List<DecorProvider>> pair =
                             DecorProviderKt.partitionAlignedBound(decorProviders, i);
                     decorProviders = pair.getSecond();
-                    createOverlay(i, cutout, pair.getFirst(), isOnlyPrivacyDotInSwLayer);
+                    createOverlay(i, pair.getFirst(), isOnlyPrivacyDotInSwLayer);
                 } else {
                     removeOverlay(i);
                 }
@@ -560,7 +628,6 @@
 
     private void createOverlay(
             @BoundsPosition int pos,
-            @Nullable DisplayCutout cutout,
             @NonNull List<DecorProvider> decorProviders,
             boolean isOnlyPrivacyDotInSwLayer) {
         if (mOverlays == null) {
@@ -568,14 +635,12 @@
         }
 
         if (mOverlays[pos] != null) {
-            // When mOverlay[pos] is not null and only privacy dot in sw layer, use privacy dot
-            // view's visibility
-            mOverlays[pos].getRootView().setVisibility(
-                    getWindowVisibility(mOverlays[pos], isOnlyPrivacyDotInSwLayer));
+            initOverlay(mOverlays[pos], decorProviders, isOnlyPrivacyDotInSwLayer);
             return;
         }
 
-        mOverlays[pos] = overlayForPosition(pos, decorProviders, isOnlyPrivacyDotInSwLayer);
+        mOverlays[pos] = new OverlayWindow(mContext);
+        initOverlay(mOverlays[pos], decorProviders, isOnlyPrivacyDotInSwLayer);
         final ViewGroup overlayView = mOverlays[pos].getRootView();
         overlayView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
         overlayView.setAlpha(0);
@@ -590,7 +655,7 @@
             mCutoutViews[pos] = new DisplayCutoutView(mContext, pos);
             mCutoutViews[pos].setColor(mTintColor);
             overlayView.addView(mCutoutViews[pos]);
-            updateView(pos, cutout);
+            mCutoutViews[pos].updateRotation(mRotation);
         }
 
         mWindowManager.addView(overlayView, getWindowLayoutParams(pos));
@@ -641,42 +706,24 @@
     }
 
     /**
-     * Allow overrides for top/bottom positions
+     * Init OverlayWindow with decorProviders
      */
-    private OverlayWindow overlayForPosition(
-            @BoundsPosition int pos,
+    private void initOverlay(
+            @NonNull OverlayWindow overlay,
             @NonNull List<DecorProvider> decorProviders,
             boolean isOnlyPrivacyDotInSwLayer) {
-        final OverlayWindow currentOverlay = new OverlayWindow(LayoutInflater.from(mContext), pos);
-        decorProviders.forEach(provider -> {
-            removeOverlayView(provider.getViewId());
-            currentOverlay.addDecorProvider(provider, mRotation);
-        });
-        // When only privacy dot in mOverlay, set the initial visibility of mOverlays to
-        // INVISIBLE and set it to VISIBLE when the privacy dot is showing.
-        if (isOnlyPrivacyDotInSwLayer) {
-            currentOverlay.getRootView().setVisibility(View.INVISIBLE);
+        if (!overlay.hasSameProviders(decorProviders)) {
+            decorProviders.forEach(provider -> {
+                if (overlay.getView(provider.getViewId()) != null) {
+                    return;
+                }
+                removeOverlayView(provider.getViewId());
+                overlay.addDecorProvider(provider, mRotation);
+            });
         }
-        return currentOverlay;
-    }
-
-    private void updateView(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
-        if (mOverlays == null || mOverlays[pos] == null || mHwcScreenDecorationSupport != null) {
-            return;
-        }
-
-        // update rounded corner view rotation
-        updateRoundedCornerView(pos, R.id.left, cutout);
-        updateRoundedCornerView(pos, R.id.right, cutout);
-        updateRoundedCornerSize(
-                mRoundedCornerResDelegate.getTopRoundedSize(),
-                mRoundedCornerResDelegate.getBottomRoundedSize());
-        updateRoundedCornerImageView();
-
-        // update cutout view rotation
-        if (mCutoutViews != null && mCutoutViews[pos] != null) {
-            mCutoutViews[pos].updateRotation(mRotation);
-        }
+        // Use visibility of privacy dot views if only privacy dot in sw layer
+        overlay.getRootView().setVisibility(
+                getWindowVisibility(overlay, isOnlyPrivacyDotInSwLayer));
     }
 
     @VisibleForTesting
@@ -849,7 +896,6 @@
             int oldRotation = mRotation;
             mPendingRotationChange = false;
             updateOrientation();
-            updateRoundedCornerRadii();
             if (DEBUG) Log.i(TAG, "onConfigChanged from rot " + oldRotation + " to " + mRotation);
             setupDecorations();
             if (mOverlays != null) {
@@ -910,109 +956,32 @@
             mDotViewController.setNewRotation(newRotation);
         }
 
-        if (mPendingRotationChange) {
-            return;
-        }
-        if (newRotation != mRotation) {
+        if (!mPendingRotationChange && newRotation != mRotation) {
             mRotation = newRotation;
             if (mScreenDecorHwcLayer != null) {
                 mScreenDecorHwcLayer.pendingRotationChange = false;
                 mScreenDecorHwcLayer.updateRotation(mRotation);
+                updateHwLayerRoundedCornerSize();
+                updateHwLayerRoundedCornerDrawable();
             }
-            if (mOverlays != null) {
-                updateLayoutParams();
-                final DisplayCutout cutout = getCutout();
-                for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
-                    if (mOverlays[i] == null) {
+            updateLayoutParams();
+            // update cutout view rotation
+            if (mCutoutViews != null) {
+                for (final DisplayCutoutView cutoutView: mCutoutViews) {
+                    if (cutoutView == null) {
                         continue;
                     }
-                    updateView(i, cutout);
+                    cutoutView.updateRotation(mRotation);
                 }
             }
         }
+
+        // update all provider views inside overlay
+        updateOverlayProviderViews();
     }
 
-    private void updateRoundedCornerRadii() {
-        // We should eventually move to just using the intrinsic size of the drawables since
-        // they should be sized to the exact pixels they want to cover. Therefore I'm purposely not
-        // upgrading all of the configs to contain (width, height) pairs. Instead assume that a
-        // device configured using the single integer config value is okay with drawing the corners
-        // as a square
-        final Size oldRoundedDefaultTop = mRoundedCornerResDelegate.getTopRoundedSize();
-        final Size oldRoundedDefaultBottom = mRoundedCornerResDelegate.getBottomRoundedSize();
-        mRoundedCornerResDelegate.reloadAll(mDisplayUniqueId);
-        final Size newRoundedDefaultTop = mRoundedCornerResDelegate.getTopRoundedSize();
-        final Size newRoundedDefaultBottom = mRoundedCornerResDelegate.getBottomRoundedSize();
-
-        if (oldRoundedDefaultTop.getWidth() != newRoundedDefaultTop.getWidth()
-                || oldRoundedDefaultBottom.getWidth() != newRoundedDefaultBottom.getWidth()) {
-            onTuningChanged(SIZE, null);
-        }
-    }
-
-    private void updateRoundedCornerView(@BoundsPosition int pos, int id,
-            @Nullable DisplayCutout cutout) {
-        final View rounded = mOverlays[pos].getRootView().findViewById(id);
-        if (rounded == null) {
-            return;
-        }
-        rounded.setVisibility(View.GONE);
-        if (shouldShowSwLayerRoundedCorner(pos, cutout)) {
-            final int gravity = getRoundedCornerGravity(pos, id == R.id.left);
-            ((FrameLayout.LayoutParams) rounded.getLayoutParams()).gravity = gravity;
-            setRoundedCornerOrientation(rounded, gravity);
-            rounded.setVisibility(View.VISIBLE);
-        }
-    }
-
-    private int getRoundedCornerGravity(@BoundsPosition int pos, boolean isStart) {
-        final int rotatedPos = getBoundPositionFromRotation(pos, mRotation);
-        switch (rotatedPos) {
-            case BOUNDS_POSITION_LEFT:
-                return isStart ? Gravity.TOP | Gravity.LEFT : Gravity.BOTTOM | Gravity.LEFT;
-            case BOUNDS_POSITION_TOP:
-                return isStart ? Gravity.TOP | Gravity.LEFT : Gravity.TOP | Gravity.RIGHT;
-            case BOUNDS_POSITION_RIGHT:
-                return isStart ? Gravity.TOP | Gravity.RIGHT : Gravity.BOTTOM | Gravity.RIGHT;
-            case BOUNDS_POSITION_BOTTOM:
-                return isStart ? Gravity.BOTTOM | Gravity.LEFT : Gravity.BOTTOM | Gravity.RIGHT;
-            default:
-                throw new IllegalArgumentException("Incorrect position: " + rotatedPos);
-        }
-    }
-
-    /**
-     * Configures the rounded corner drawable's view matrix based on the gravity.
-     *
-     * The gravity describes which corner to configure for, and the drawable we are rotating is
-     * assumed to be oriented for the top-left corner of the device regardless of the target corner.
-     * Therefore we need to rotate 180 degrees to get a bottom-left corner, and mirror in the x- or
-     * y-axis for the top-right and bottom-left corners.
-     */
-    private void setRoundedCornerOrientation(View corner, int gravity) {
-        corner.setRotation(0);
-        corner.setScaleX(1);
-        corner.setScaleY(1);
-        switch (gravity) {
-            case Gravity.TOP | Gravity.LEFT:
-                return;
-            case Gravity.TOP | Gravity.RIGHT:
-                corner.setScaleX(-1); // flip X axis
-                return;
-            case Gravity.BOTTOM | Gravity.LEFT:
-                corner.setScaleY(-1); // flip Y axis
-                return;
-            case Gravity.BOTTOM | Gravity.RIGHT:
-                corner.setRotation(180);
-                return;
-            default:
-                throw new IllegalArgumentException("Unsupported gravity: " + gravity);
-        }
-    }
     private boolean hasRoundedCorners() {
-        return mRoundedCornerResDelegate.getBottomRoundedSize().getWidth() > 0
-                || mRoundedCornerResDelegate.getTopRoundedSize().getWidth() > 0
-                || mRoundedCornerResDelegate.isMultipleRadius();
+        return mRoundedCornerFactory.getHasProviders();
     }
 
     private boolean isDefaultShownOverlayPos(@BoundsPosition int pos,
@@ -1066,6 +1035,19 @@
                 context.getResources(), context.getDisplay().getUniqueId());
     }
 
+    private void updateOverlayProviderViews() {
+        if (mOverlays == null) {
+            return;
+        }
+        ++mProviderRefreshToken;
+        for (final OverlayWindow overlay: mOverlays) {
+            if (overlay == null) {
+                continue;
+            }
+            overlay.onReloadResAndMeasure(null, mProviderRefreshToken, mRotation, mDisplayUniqueId);
+        }
+    }
+
     private void updateLayoutParams() {
         if (mOverlays == null) {
             return;
@@ -1085,63 +1067,33 @@
             return;
         }
         mExecutor.execute(() -> {
-            if (mOverlays == null) return;
-            if (SIZE.equals(key)) {
-                boolean hasReloadRoundedCornerRes = false;
-                if (newValue != null) {
-                    try {
-                        mRoundedCornerResDelegate.updateTuningSizeFactor(
-                                Integer.parseInt(newValue));
-                        hasReloadRoundedCornerRes = true;
-                    } catch (Exception e) {
-                    }
-                }
-
-                // When onTuningChanged() is not called through updateRoundedCornerRadii(),
-                // we need to reload rounded corner res to prevent incorrect dimen
-                if (!hasReloadRoundedCornerRes) {
-                    mRoundedCornerResDelegate.reloadAll(mDisplayUniqueId);
-                }
-
-                updateRoundedCornerSize(
-                        mRoundedCornerResDelegate.getTopRoundedSize(),
-                        mRoundedCornerResDelegate.getBottomRoundedSize());
+            if (mOverlays == null || !SIZE.equals(key)) {
+                return;
             }
+            ++mProviderRefreshToken;
+            try {
+                final int sizeFactor = Integer.parseInt(newValue);
+                mRoundedCornerResDelegate.updateTuningSizeFactor(sizeFactor, mProviderRefreshToken);
+            } catch (NumberFormatException e) {
+                mRoundedCornerResDelegate.updateTuningSizeFactor(null, mProviderRefreshToken);
+            }
+            Integer[] filterIds = {
+                    R.id.rounded_corner_top_left,
+                    R.id.rounded_corner_top_right,
+                    R.id.rounded_corner_bottom_left,
+                    R.id.rounded_corner_bottom_right
+            };
+            for (final OverlayWindow overlay: mOverlays) {
+                if (overlay == null) {
+                    continue;
+                }
+                overlay.onReloadResAndMeasure(filterIds, mProviderRefreshToken, mRotation,
+                        mDisplayUniqueId);
+            }
+            updateHwLayerRoundedCornerSize();
         });
     }
 
-    private void updateRoundedCornerDrawable() {
-        mRoundedCornerResDelegate.reloadAll(mDisplayUniqueId);
-        updateRoundedCornerImageView();
-    }
-
-    private void updateRoundedCornerImageView() {
-        final Drawable top = mRoundedCornerResDelegate.getTopRoundedDrawable();
-        final Drawable bottom = mRoundedCornerResDelegate.getBottomRoundedDrawable();
-
-        if (mScreenDecorHwcLayer != null) {
-            mScreenDecorHwcLayer.updateRoundedCornerDrawable(top, bottom);
-            return;
-        }
-
-        if (mOverlays == null) {
-            return;
-        }
-        final ColorStateList colorStateList = ColorStateList.valueOf(mTintColor);
-        for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
-            if (mOverlays[i] == null) {
-                continue;
-            }
-            final ViewGroup overlayView = mOverlays[i].getRootView();
-            ((ImageView) overlayView.findViewById(R.id.left)).setImageTintList(colorStateList);
-            ((ImageView) overlayView.findViewById(R.id.right)).setImageTintList(colorStateList);
-            ((ImageView) overlayView.findViewById(R.id.left)).setImageDrawable(
-                    isTopRoundedCorner(i, R.id.left) ? top : bottom);
-            ((ImageView) overlayView.findViewById(R.id.right)).setImageDrawable(
-                    isTopRoundedCorner(i, R.id.right) ? top : bottom);
-        }
-    }
-
     private void updateHwLayerRoundedCornerDrawable() {
         if (mScreenDecorHwcLayer == null) {
             return;
@@ -1156,25 +1108,6 @@
         mScreenDecorHwcLayer.updateRoundedCornerDrawable(topDrawable, bottomDrawable);
     }
 
-    @VisibleForTesting
-    boolean isTopRoundedCorner(@BoundsPosition int pos, int id) {
-        switch (pos) {
-            case BOUNDS_POSITION_LEFT:
-            case BOUNDS_POSITION_RIGHT:
-                if (mRotation == ROTATION_270) {
-                    return id == R.id.left ? false : true;
-                } else {
-                    return id == R.id.left ? true : false;
-                }
-            case BOUNDS_POSITION_TOP:
-                return true;
-            case BOUNDS_POSITION_BOTTOM:
-                return false;
-            default:
-                throw new IllegalArgumentException("Unknown bounds position");
-        }
-    }
-
     private void updateHwLayerRoundedCornerSize() {
         if (mScreenDecorHwcLayer == null) {
             return;
@@ -1186,28 +1119,6 @@
         mScreenDecorHwcLayer.updateRoundedCornerSize(topWidth, bottomWidth);
     }
 
-    private void updateRoundedCornerSize(Size sizeTop, Size sizeBottom) {
-
-        if (mScreenDecorHwcLayer != null) {
-            mScreenDecorHwcLayer.updateRoundedCornerSize(sizeTop.getWidth(), sizeBottom.getWidth());
-            return;
-        }
-
-        if (mOverlays == null) {
-            return;
-        }
-        for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
-            if (mOverlays[i] == null) {
-                continue;
-            }
-            final ViewGroup overlayView = mOverlays[i].getRootView();
-            setSize(overlayView.findViewById(R.id.left),
-                    isTopRoundedCorner(i, R.id.left) ? sizeTop : sizeBottom);
-            setSize(overlayView.findViewById(R.id.right),
-                    isTopRoundedCorner(i, R.id.right) ? sizeTop : sizeBottom);
-        }
-    }
-
     @VisibleForTesting
     protected void setSize(View view, Size pixelSize) {
         LayoutParams params = view.getLayoutParams();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index b2673e9..233f364 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -56,7 +56,9 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.util.concurrency.DelayableExecutor;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -110,6 +112,8 @@
     @ContainerState private int mContainerState = STATE_UNKNOWN;
     private final Set<Integer> mFailedModalities = new HashSet<Integer>();
 
+    private final @Background DelayableExecutor mBackgroundExecutor;
+
     // Non-null only if the dialog is in the act of dismissing and has not sent the reason yet.
     @Nullable @AuthDialogCallback.DismissedReason private Integer mPendingCallbackReason;
     // HAT received from LockSettingsService when credential is verified.
@@ -192,7 +196,7 @@
             return this;
         }
 
-        public AuthContainerView build(int[] sensorIds,
+        public AuthContainerView build(@Background DelayableExecutor bgExecutor, int[] sensorIds,
                 @Nullable List<FingerprintSensorPropertiesInternal> fpProps,
                 @Nullable List<FaceSensorPropertiesInternal> faceProps,
                 @NonNull WakefulnessLifecycle wakefulnessLifecycle,
@@ -200,7 +204,7 @@
                 @NonNull LockPatternUtils lockPatternUtils) {
             mConfig.mSensorIds = sensorIds;
             return new AuthContainerView(mConfig, fpProps, faceProps, wakefulnessLifecycle,
-                    userManager, lockPatternUtils, new Handler(Looper.getMainLooper()));
+                    userManager, lockPatternUtils, new Handler(Looper.getMainLooper()), bgExecutor);
         }
     }
 
@@ -253,7 +257,8 @@
             @NonNull WakefulnessLifecycle wakefulnessLifecycle,
             @NonNull UserManager userManager,
             @NonNull LockPatternUtils lockPatternUtils,
-            @NonNull Handler mainHandler) {
+            @NonNull Handler mainHandler,
+            @NonNull @Background DelayableExecutor bgExecutor) {
         super(config.mContext);
 
         mConfig = config;
@@ -277,6 +282,7 @@
         mBackgroundView = mFrameLayout.findViewById(R.id.background);
         mPanelView = mFrameLayout.findViewById(R.id.panel);
         mPanelController = new AuthPanelController(mContext, mPanelView);
+        mBackgroundExecutor = bgExecutor;
 
         // Inflate biometric view only if necessary.
         if (Utils.isBiometricAllowed(mConfig.mPromptInfo)) {
@@ -384,6 +390,7 @@
         mCredentialView.setPromptInfo(mConfig.mPromptInfo);
         mCredentialView.setPanelController(mPanelController, animatePanel);
         mCredentialView.setShouldAnimateContents(animateContents);
+        mCredentialView.setBackgroundExecutor(mBackgroundExecutor);
         mFrameLayout.addView(mCredentialView);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index aaf18b3..b05bc24 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -64,11 +64,13 @@
 import com.android.systemui.CoreStartable;
 import com.android.systemui.assist.ui.DisplayUtils;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.DozeReceiver;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.concurrency.Execution;
 
 import java.util.ArrayList;
@@ -139,6 +141,7 @@
     private boolean mAllAuthenticatorsRegistered;
     @NonNull private final UserManager mUserManager;
     @NonNull private final LockPatternUtils mLockPatternUtils;
+    private final @Background DelayableExecutor mBackgroundExecutor;
 
     @VisibleForTesting
     final TaskStackListener mTaskStackListener = new TaskStackListener() {
@@ -507,13 +510,15 @@
             @NonNull UserManager userManager,
             @NonNull LockPatternUtils lockPatternUtils,
             @NonNull StatusBarStateController statusBarStateController,
-            @Main Handler handler) {
+            @Main Handler handler,
+            @Background DelayableExecutor bgExecutor) {
         super(context);
         mExecution = execution;
         mWakefulnessLifecycle = wakefulnessLifecycle;
         mUserManager = userManager;
         mLockPatternUtils = lockPatternUtils;
         mHandler = handler;
+        mBackgroundExecutor = bgExecutor;
         mCommandQueue = commandQueue;
         mActivityTaskManager = activityTaskManager;
         mFingerprintManager = fingerprintManager;
@@ -839,6 +844,7 @@
 
         // Create a new dialog but do not replace the current one yet.
         final AuthDialog newDialog = buildDialog(
+                mBackgroundExecutor,
                 promptInfo,
                 requireConfirmation,
                 userId,
@@ -934,9 +940,9 @@
         }
     }
 
-    protected AuthDialog buildDialog(PromptInfo promptInfo, boolean requireConfirmation,
-            int userId, int[] sensorIds, String opPackageName,
-            boolean skipIntro, long operationId, long requestId,
+    protected AuthDialog buildDialog(@Background DelayableExecutor bgExecutor,
+            PromptInfo promptInfo, boolean requireConfirmation, int userId, int[] sensorIds,
+            String opPackageName, boolean skipIntro, long operationId, long requestId,
             @BiometricMultiSensorMode int multiSensorConfig,
             @NonNull WakefulnessLifecycle wakefulnessLifecycle,
             @NonNull UserManager userManager,
@@ -951,8 +957,8 @@
                 .setOperationId(operationId)
                 .setRequestId(requestId)
                 .setMultiSensorConfig(multiSensorConfig)
-                .build(sensorIds, mFpProps, mFaceProps, wakefulnessLifecycle, userManager,
-                        lockPatternUtils);
+                .build(bgExecutor, sensorIds, mFpProps, mFaceProps, wakefulnessLifecycle,
+                        userManager, lockPatternUtils);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index ed84a37..4fa835e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -53,6 +53,8 @@
 import com.android.internal.widget.VerifyCredentialResponse;
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.util.concurrency.DelayableExecutor;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -99,6 +101,8 @@
     protected int mEffectiveUserId;
     protected ErrorTimer mErrorTimer;
 
+    protected @Background DelayableExecutor mBackgroundExecutor;
+
     interface Callback {
         void onCredentialMatched(byte[] attestation);
     }
@@ -217,6 +221,10 @@
         mContainerView = containerView;
     }
 
+    void setBackgroundExecutor(@Background DelayableExecutor bgExecutor) {
+        mBackgroundExecutor = bgExecutor;
+    }
+
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
@@ -377,27 +385,33 @@
     }
 
     private void showLastAttemptBeforeWipeDialog() {
-        final AlertDialog alertDialog = new AlertDialog.Builder(mContext)
-                .setTitle(R.string.biometric_dialog_last_attempt_before_wipe_dialog_title)
-                .setMessage(
-                        getLastAttemptBeforeWipeMessage(getUserTypeForWipe(), mCredentialType))
-                .setPositiveButton(android.R.string.ok, null)
-                .create();
-        alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
-        alertDialog.show();
+        mBackgroundExecutor.execute(() -> {
+            final AlertDialog alertDialog = new AlertDialog.Builder(mContext)
+                    .setTitle(R.string.biometric_dialog_last_attempt_before_wipe_dialog_title)
+                    .setMessage(
+                            getLastAttemptBeforeWipeMessage(getUserTypeForWipe(), mCredentialType))
+                    .setPositiveButton(android.R.string.ok, null)
+                    .create();
+            alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
+            mHandler.post(alertDialog::show);
+        });
     }
 
     private void showNowWipingDialog() {
-        final AlertDialog alertDialog = new AlertDialog.Builder(mContext)
-                .setMessage(getNowWipingMessage(getUserTypeForWipe()))
-                .setPositiveButton(
-                        com.android.settingslib.R.string.failed_attempts_now_wiping_dialog_dismiss,
-                        null /* OnClickListener */)
-                .setOnDismissListener(
-                        dialog -> mContainerView.animateAway(AuthDialogCallback.DISMISSED_ERROR))
-                .create();
-        alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
-        alertDialog.show();
+        mBackgroundExecutor.execute(() -> {
+            String nowWipingMessage = getNowWipingMessage(getUserTypeForWipe());
+            final AlertDialog alertDialog = new AlertDialog.Builder(mContext)
+                    .setMessage(nowWipingMessage)
+                    .setPositiveButton(
+                            com.android.settingslib.R.string.failed_attempts_now_wiping_dialog_dismiss,
+                            null /* OnClickListener */)
+                    .setOnDismissListener(
+                            dialog -> mContainerView.animateAway(
+                                    AuthDialogCallback.DISMISSED_ERROR))
+                    .create();
+            alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
+            mHandler.post(alertDialog::show);
+        });
     }
 
     private @UserType int getUserTypeForWipe() {
@@ -412,6 +426,7 @@
         }
     }
 
+    // This should not be called on the main thread to avoid making an IPC.
     private String getLastAttemptBeforeWipeMessage(
             @UserType int userType, @Utils.CredentialType int credentialType) {
         switch (userType) {
@@ -442,6 +457,7 @@
         }
     }
 
+    // This should not be called on the main thread to avoid making an IPC.
     private String getLastAttemptBeforeWipeProfileMessage(
             @Utils.CredentialType int credentialType) {
         return mDevicePolicyManager.getResources().getString(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 33126b3..76c1dbc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -145,6 +145,7 @@
         val lightRevealScrim = centralSurfaces.lightRevealScrim
         if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
             circleReveal?.let {
+                lightRevealScrim?.revealAmount = 0f
                 lightRevealScrim?.revealEffect = it
                 startLightRevealScrimOnKeyguardFadingAway = true
             }
@@ -168,7 +169,8 @@
                     startDelay = keyguardStateController.keyguardFadingAwayDelay
                     addUpdateListener { animator ->
                         if (lightRevealScrim.revealEffect != circleReveal) {
-                            // if something else took over the reveal, let's do nothing.
+                            // if something else took over the reveal, let's cancel ourselves
+                            cancel()
                             return@addUpdateListener
                         }
                         lightRevealScrim.revealAmount = animator.animatedValue as Float
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 5d131f2..f5f07c8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -25,6 +25,7 @@
 import android.util.MathUtils;
 import android.view.MotionEvent;
 
+import com.android.keyguard.BouncerPanelExpansionCalculator;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.R;
 import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -71,7 +72,7 @@
     private float mTransitionToFullShadeProgress;
     private float mLastDozeAmount;
     private long mLastUdfpsBouncerShowTime = -1;
-    private float mStatusBarExpansion;
+    private float mPanelExpansionFraction;
     private boolean mLaunchTransitionFadingAway;
     private boolean mIsLaunchingActivity;
     private float mActivityLaunchProgress;
@@ -188,7 +189,7 @@
         pw.println("mQsExpanded=" + mQsExpanded);
         pw.println("mIsBouncerVisible=" + mIsBouncerVisible);
         pw.println("mInputBouncerHiddenAmount=" + mInputBouncerHiddenAmount);
-        pw.println("mStatusBarExpansion=" + mStatusBarExpansion);
+        pw.println("mPanelExpansionFraction=" + mPanelExpansionFraction);
         pw.println("unpausedAlpha=" + mView.getUnpausedAlpha());
         pw.println("mUdfpsRequested=" + mUdfpsRequested);
         pw.println("mView.mUdfpsRequested=" + mView.mUdfpsRequested);
@@ -324,14 +325,16 @@
      */
     @Override
     public void updateAlpha() {
-        // fade icon on transitions to showing the status bar, but if mUdfpsRequested, then
-        // the keyguard is occluded by some application - so instead use the input bouncer
-        // hidden amount to determine the fade
-        float expansion = mUdfpsRequested ? mInputBouncerHiddenAmount : mStatusBarExpansion;
+        // Fade icon on transitions to showing the status bar or bouncer, but if mUdfpsRequested,
+        // then the keyguard is occluded by some application - so instead use the input bouncer
+        // hidden amount to determine the fade.
+        float expansion = mUdfpsRequested ? mInputBouncerHiddenAmount : mPanelExpansionFraction;
+
         int alpha = mShowingUdfpsBouncer ? 255
                 : (int) MathUtils.constrain(
                     MathUtils.map(.5f, .9f, 0f, 255f, expansion),
                     0f, 255f);
+
         if (!mShowingUdfpsBouncer) {
             alpha *= (1.0f - mTransitionToFullShadeProgress);
 
@@ -471,7 +474,9 @@
         @Override
         public void onPanelExpansionChanged(
                 float fraction, boolean expanded, boolean tracking) {
-            mStatusBarExpansion = fraction;
+            mPanelExpansionFraction =
+                    mKeyguardViewManager.bouncerIsInTransit() ? BouncerPanelExpansionCalculator
+                            .aboutToShowBouncerProgress(fraction) : fraction;
             updateAlpha();
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/ActionReceiver.kt b/packages/SystemUI/src/com/android/systemui/broadcast/ActionReceiver.kt
index 4315cb0..ca36375 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/ActionReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/ActionReceiver.kt
@@ -39,6 +39,13 @@
  *
  * This class has no sync controls, so make sure to only make modifications from the background
  * thread.
+ *
+ * This class takes the following actions:
+ * * [registerAction]: action to register this receiver (with the proper filter) with [Context].
+ * * [unregisterAction]: action to unregister this receiver with [Context].
+ * * [testPendingRemovalAction]: action to check if a particular [BroadcastReceiver] registered
+ *   with [BroadcastDispatcher] has been unregistered and is pending removal. See
+ *   [PendingRemovalStore].
  */
 class ActionReceiver(
     private val action: String,
@@ -46,7 +53,8 @@
     private val registerAction: BroadcastReceiver.(IntentFilter) -> Unit,
     private val unregisterAction: BroadcastReceiver.() -> Unit,
     private val bgExecutor: Executor,
-    private val logger: BroadcastDispatcherLogger
+    private val logger: BroadcastDispatcherLogger,
+    private val testPendingRemovalAction: (BroadcastReceiver, Int) -> Boolean
 ) : BroadcastReceiver(), Dumpable {
 
     companion object {
@@ -106,7 +114,8 @@
         // Immediately return control to ActivityManager
         bgExecutor.execute {
             receiverDatas.forEach {
-                if (it.filter.matchCategories(intent.categories) == null) {
+                if (it.filter.matchCategories(intent.categories) == null &&
+                    !testPendingRemovalAction(it.receiver, userId)) {
                     it.executor.execute {
                         it.receiver.pendingResult = pendingResult
                         it.receiver.onReceive(context, intent)
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index 1c27e32..b7aebc1 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -63,13 +63,14 @@
  * Broadcast handling may be asynchronous *without* calling goAsync(), as it's running within sysui
  * and doesn't need to worry about being killed.
  */
-open class BroadcastDispatcher constructor (
+open class BroadcastDispatcher @JvmOverloads constructor (
     private val context: Context,
     private val bgLooper: Looper,
     private val bgExecutor: Executor,
     private val dumpManager: DumpManager,
     private val logger: BroadcastDispatcherLogger,
-    private val userTracker: UserTracker
+    private val userTracker: UserTracker,
+    private val removalPendingStore: PendingRemovalStore = PendingRemovalStore(logger)
 ) : Dumpable {
 
     // Only modify in BG thread
@@ -167,6 +168,7 @@
      * @param receiver The receiver to unregister. It will be unregistered for all users.
      */
     open fun unregisterReceiver(receiver: BroadcastReceiver) {
+        removalPendingStore.tagForRemoval(receiver, UserHandle.USER_ALL)
         handler.obtainMessage(MSG_REMOVE_RECEIVER, receiver).sendToTarget()
     }
 
@@ -177,13 +179,21 @@
      * @param user The user associated to the registered [receiver]. It can be [UserHandle.ALL].
      */
     open fun unregisterReceiverForUser(receiver: BroadcastReceiver, user: UserHandle) {
+        removalPendingStore.tagForRemoval(receiver, user.identifier)
         handler.obtainMessage(MSG_REMOVE_RECEIVER_FOR_USER, user.identifier, 0, receiver)
                 .sendToTarget()
     }
 
     @VisibleForTesting
     protected open fun createUBRForUser(userId: Int) =
-            UserBroadcastDispatcher(context, userId, bgLooper, bgExecutor, logger)
+            UserBroadcastDispatcher(
+                context,
+                userId,
+                bgLooper,
+                bgExecutor,
+                logger,
+                removalPendingStore
+            )
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
         pw.println("Broadcast dispatcher:")
@@ -193,6 +203,8 @@
             ipw.println("User ${receiversByUser.keyAt(index)}")
             receiversByUser.valueAt(index).dump(ipw, args)
         }
+        ipw.println("Pending removal:")
+        removalPendingStore.dump(ipw, args)
         ipw.decreaseIndent()
     }
 
@@ -223,10 +235,20 @@
                     for (it in 0 until receiversByUser.size()) {
                         receiversByUser.valueAt(it).unregisterReceiver(msg.obj as BroadcastReceiver)
                     }
+                    removalPendingStore.clearPendingRemoval(
+                        msg.obj as BroadcastReceiver,
+                        UserHandle.USER_ALL
+                    )
                 }
 
                 MSG_REMOVE_RECEIVER_FOR_USER -> {
-                    receiversByUser.get(msg.arg1)?.unregisterReceiver(msg.obj as BroadcastReceiver)
+                    val userId = if (msg.arg1 == UserHandle.USER_CURRENT) {
+                        userTracker.userId
+                    } else {
+                        msg.arg1
+                    }
+                    receiversByUser.get(userId)?.unregisterReceiver(msg.obj as BroadcastReceiver)
+                    removalPendingStore.clearPendingRemoval(msg.obj as BroadcastReceiver, userId)
                 }
                 else -> super.handleMessage(msg)
             }
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/PendingRemovalStore.kt b/packages/SystemUI/src/com/android/systemui/broadcast/PendingRemovalStore.kt
new file mode 100644
index 0000000..ebf4983
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/PendingRemovalStore.kt
@@ -0,0 +1,58 @@
+package com.android.systemui.broadcast
+
+import android.content.BroadcastReceiver
+import android.os.UserHandle
+import android.util.SparseSetArray
+import androidx.annotation.GuardedBy
+import com.android.systemui.Dumpable
+import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
+import com.android.systemui.util.indentIfPossible
+import java.io.PrintWriter
+
+/**
+ * Store information about requests for unregistering receivers from [BroadcastDispatcher], before
+ * they have been completely removed from the system.
+ *
+ * This helps make unregistering a receiver a *sync* operation.
+ */
+class PendingRemovalStore(
+    private val logger: BroadcastDispatcherLogger
+) : Dumpable {
+    @GuardedBy("pendingRemoval")
+    private val pendingRemoval: SparseSetArray<BroadcastReceiver> = SparseSetArray()
+
+    fun tagForRemoval(broadcastReceiver: BroadcastReceiver, userId: Int) {
+        logger.logTagForRemoval(userId, broadcastReceiver)
+        synchronized(pendingRemoval) {
+            pendingRemoval.add(userId, broadcastReceiver)
+        }
+    }
+
+    fun isPendingRemoval(broadcastReceiver: BroadcastReceiver, userId: Int): Boolean {
+        return synchronized(pendingRemoval) {
+            pendingRemoval.contains(userId, broadcastReceiver) ||
+                pendingRemoval.contains(UserHandle.USER_ALL, broadcastReceiver)
+        }
+    }
+
+    fun clearPendingRemoval(broadcastReceiver: BroadcastReceiver, userId: Int) {
+        synchronized(pendingRemoval) {
+            pendingRemoval.remove(userId, broadcastReceiver)
+        }
+        logger.logClearedAfterRemoval(userId, broadcastReceiver)
+    }
+
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        synchronized(pendingRemoval) {
+            pw.indentIfPossible {
+                val size = pendingRemoval.size()
+                for (i in 0 until size) {
+                    val user = pendingRemoval.keyAt(i)
+                    print(user)
+                    print("->")
+                    println(pendingRemoval.get(user))
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
index 24ce238..6b15188 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
@@ -20,12 +20,12 @@
 import android.content.Context
 import android.os.Handler
 import android.os.Looper
-import android.os.Message
 import android.os.UserHandle
 import android.util.ArrayMap
 import android.util.ArraySet
 import android.util.Log
 import androidx.annotation.VisibleForTesting
+import androidx.annotation.WorkerThread
 import com.android.internal.util.Preconditions
 import com.android.systemui.Dumpable
 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
@@ -34,8 +34,6 @@
 import java.util.concurrent.Executor
 import java.util.concurrent.atomic.AtomicInteger
 
-private const val MSG_REGISTER_RECEIVER = 0
-private const val MSG_UNREGISTER_RECEIVER = 1
 private const val TAG = "UserBroadcastDispatcher"
 private const val DEBUG = false
 
@@ -50,7 +48,8 @@
     private val userId: Int,
     private val bgLooper: Looper,
     private val bgExecutor: Executor,
-    private val logger: BroadcastDispatcherLogger
+    private val logger: BroadcastDispatcherLogger,
+    private val removalPendingStore: PendingRemovalStore
 ) : Dumpable {
 
     companion object {
@@ -60,16 +59,6 @@
         val index = AtomicInteger(0)
     }
 
-    private val bgHandler = object : Handler(bgLooper) {
-        override fun handleMessage(msg: Message) {
-            when (msg.what) {
-                MSG_REGISTER_RECEIVER -> handleRegisterReceiver(msg.obj as ReceiverData, msg.arg1)
-                MSG_UNREGISTER_RECEIVER -> handleUnregisterReceiver(msg.obj as BroadcastReceiver)
-                else -> Unit
-            }
-        }
-    }
-
     // Used for key in actionsToActionsReceivers
     internal data class ReceiverProperties(
         val action: String,
@@ -77,6 +66,8 @@
         val permission: String?
     )
 
+    private val bgHandler = Handler(bgLooper)
+
     // Only modify in BG thread
     @VisibleForTesting
     internal val actionsToActionsReceivers = ArrayMap<ReceiverProperties, ActionReceiver>()
@@ -92,19 +83,21 @@
     /**
      * Register a [ReceiverData] for this user.
      */
+    @WorkerThread
     fun registerReceiver(receiverData: ReceiverData, flags: Int) {
-        bgHandler.obtainMessage(MSG_REGISTER_RECEIVER, flags, 0, receiverData).sendToTarget()
+        handleRegisterReceiver(receiverData, flags)
     }
 
     /**
      * Unregister a given [BroadcastReceiver] for this user.
      */
+    @WorkerThread
     fun unregisterReceiver(receiver: BroadcastReceiver) {
-        bgHandler.obtainMessage(MSG_UNREGISTER_RECEIVER, receiver).sendToTarget()
+        handleUnregisterReceiver(receiver)
     }
 
     private fun handleRegisterReceiver(receiverData: ReceiverData, flags: Int) {
-        Preconditions.checkState(bgHandler.looper.isCurrentThread,
+        Preconditions.checkState(bgLooper.isCurrentThread,
                 "This method should only be called from BG thread")
         if (DEBUG) Log.w(TAG, "Register receiver: ${receiverData.receiver}")
         receiverToActions
@@ -151,12 +144,13 @@
                     }
                 },
                 bgExecutor,
-                logger
+                logger,
+                removalPendingStore::isPendingRemoval
         )
     }
 
     private fun handleUnregisterReceiver(receiver: BroadcastReceiver) {
-        Preconditions.checkState(bgHandler.looper.isCurrentThread,
+        Preconditions.checkState(bgLooper.isCurrentThread,
                 "This method should only be called from BG thread")
         if (DEBUG) Log.w(TAG, "Unregister receiver: $receiver")
         receiverToActions.getOrDefault(receiver, mutableSetOf()).forEach {
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt b/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt
index 8da6519..5b3a982 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt
@@ -87,6 +87,26 @@
         })
     }
 
+    fun logTagForRemoval(user: Int, receiver: BroadcastReceiver) {
+        val receiverString = receiver.toString()
+        log(DEBUG, {
+            int1 = user
+            str1 = receiverString
+        }, {
+            "Receiver $str1 tagged for removal from user $int1"
+        })
+    }
+
+    fun logClearedAfterRemoval(user: Int, receiver: BroadcastReceiver) {
+        val receiverString = receiver.toString()
+        log(DEBUG, {
+            int1 = user
+            str1 = receiverString
+        }, {
+            "Receiver $str1 has been completely removed for user $int1"
+        })
+    }
+
     fun logReceiverUnregistered(user: Int, receiver: BroadcastReceiver) {
         val receiverString = receiver.toString()
         log(INFO, {
diff --git a/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt b/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt
index 3543bb4..03ee8b1 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt
@@ -15,8 +15,8 @@
  */
 
 package com.android.systemui.decor
+import android.content.Context
 import android.view.DisplayCutout
-import android.view.LayoutInflater
 import android.view.Surface
 import android.view.View
 import android.view.ViewGroup
@@ -38,9 +38,20 @@
     /** The aligned bounds for the view which is created through inflateView() */
     abstract val alignedBounds: List<Int>
 
+    /**
+     * Called when res info changed.
+     * Child provider needs to implement it if its view needs to be updated.
+     */
+    abstract fun onReloadResAndMeasure(
+        view: View,
+        reloadToken: Int,
+        @Surface.Rotation rotation: Int,
+        displayUniqueId: String? = null
+    )
+
     /** Inflate view into parent as current rotation */
     abstract fun inflateView(
-        inflater: LayoutInflater,
+        context: Context,
         parent: ViewGroup,
         @Surface.Rotation rotation: Int
     ): View
diff --git a/packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt
index c60cad8..cc4096f 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt
@@ -19,4 +19,5 @@
 abstract class DecorProviderFactory {
     abstract val providers: List<DecorProvider>
     abstract val hasProviders: Boolean
+    abstract fun onDisplayUniqueIdChanged(displayUniqueId: String?)
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt b/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt
index 9f8679c..d775ad3 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt
@@ -16,31 +16,25 @@
 package com.android.systemui.decor
 
 import android.annotation.IdRes
-import android.view.DisplayCutout
-import android.view.LayoutInflater
+import android.content.Context
 import android.view.Surface
 import android.view.View
 import android.view.ViewGroup
-import com.android.systemui.R
-import java.util.HashMap
+import com.android.systemui.RegionInterceptingFrameLayout
 
-class OverlayWindow(private val layoutInflater: LayoutInflater, private val pos: Int) {
+class OverlayWindow(private val context: Context) {
 
-    private val layoutId: Int
-    get() {
-        return if (pos == DisplayCutout.BOUNDS_POSITION_LEFT ||
-                pos == DisplayCutout.BOUNDS_POSITION_TOP) {
-            R.layout.rounded_corners_top
-        } else {
-            R.layout.rounded_corners_bottom
-        }
-    }
+    val rootView = RegionInterceptingFrameLayout(context) as ViewGroup
+    private val viewProviderMap = mutableMapOf<Int, Pair<View, DecorProvider>>()
 
-    val rootView = layoutInflater.inflate(layoutId, null) as ViewGroup
-    private val viewProviderMap: MutableMap<Int, Pair<View, DecorProvider>> = HashMap()
+    val viewIds: List<Int>
+        get() = viewProviderMap.keys.toList()
 
-    fun addDecorProvider(decorProvider: DecorProvider, @Surface.Rotation rotation: Int) {
-        val view = decorProvider.inflateView(layoutInflater, rootView, rotation)
+    fun addDecorProvider(
+        decorProvider: DecorProvider,
+        @Surface.Rotation rotation: Int
+    ) {
+        val view = decorProvider.inflateView(context, rootView, rotation)
         viewProviderMap[decorProvider.viewId] = Pair(view, decorProvider)
     }
 
@@ -56,4 +50,54 @@
             viewProviderMap.remove(id)
         }
     }
+
+    /**
+     * Remove views which does not been found in expectExistViewIds
+     */
+    fun removeRedundantViews(expectExistViewIds: IntArray?) {
+        viewIds.forEach {
+            if (expectExistViewIds == null || !(expectExistViewIds.contains(it))) {
+                removeView(it)
+            }
+        }
+    }
+
+    /**
+     * Check that newProviders is the same list with viewProviderMap.
+     */
+    fun hasSameProviders(newProviders: List<DecorProvider>): Boolean {
+        return (newProviders.size == viewProviderMap.size) &&
+                newProviders.all { getView(it.viewId) != null }
+    }
+
+    /**
+     * Apply new configuration info into views.
+     * @param filterIds target view ids. Apply to all if null.
+     * @param rotation current or new rotation direction.
+     * @param displayUniqueId new displayUniqueId if any.
+     */
+    fun onReloadResAndMeasure(
+        filterIds: Array<Int>? = null,
+        reloadToken: Int,
+        @Surface.Rotation rotation: Int,
+        displayUniqueId: String? = null
+    ) {
+        filterIds?.forEach { id ->
+            viewProviderMap[id]?.let {
+                it.second.onReloadResAndMeasure(
+                        view = it.first,
+                        reloadToken = reloadToken,
+                        displayUniqueId = displayUniqueId,
+                        rotation = rotation)
+            }
+        } ?: run {
+            viewProviderMap.values.forEach {
+                it.second.onReloadResAndMeasure(
+                        view = it.first,
+                        reloadToken = reloadToken,
+                        displayUniqueId = displayUniqueId,
+                        rotation = rotation)
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt
index 7afd7e0e..d16d960 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.decor
 
+import android.content.Context
 import android.content.res.Resources
 import android.view.DisplayCutout
 import android.view.LayoutInflater
@@ -38,6 +39,10 @@
     override val hasProviders: Boolean
         get() = isPrivacyDotEnabled
 
+    override fun onDisplayUniqueIdChanged(displayUniqueId: String?) {
+        // Do nothing for privacy dot
+    }
+
     override val providers: List<DecorProvider>
         get() {
             return if (hasProviders) {
@@ -76,12 +81,21 @@
     private val layoutId: Int
 ) : CornerDecorProvider() {
 
+    override fun onReloadResAndMeasure(
+        view: View,
+        reloadToken: Int,
+        rotation: Int,
+        displayUniqueId: String?
+    ) {
+        // Do nothing here because it is handled inside PrivacyDotViewController
+    }
+
     override fun inflateView(
-        inflater: LayoutInflater,
+        context: Context,
         parent: ViewGroup,
         @Surface.Rotation rotation: Int
     ): View {
-        inflater.inflate(layoutId, parent, true)
+        LayoutInflater.from(context).inflate(layoutId, parent, true)
         return parent.getChildAt(parent.childCount - 1 /* latest new added child */)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt
new file mode 100644
index 0000000..a4f7a58
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.decor
+
+import android.view.DisplayCutout
+import com.android.systemui.R
+
+class RoundedCornerDecorProviderFactory(
+    private val roundedCornerResDelegate: RoundedCornerResDelegate
+) : DecorProviderFactory() {
+
+    override val hasProviders: Boolean
+        get() = roundedCornerResDelegate.run {
+            // We don't consider isMultipleRadius here because it makes no sense if size is zero.
+            topRoundedSize.width > 0 || bottomRoundedSize.width > 0
+        }
+
+    override fun onDisplayUniqueIdChanged(displayUniqueId: String?) {
+        roundedCornerResDelegate.updateDisplayUniqueId(displayUniqueId, null)
+    }
+
+    override val providers: List<DecorProvider>
+    get() {
+        val hasTop = roundedCornerResDelegate.topRoundedSize.width > 0
+        val hasBottom = roundedCornerResDelegate.bottomRoundedSize.width > 0
+        return when {
+            hasTop && hasBottom -> listOf(
+                RoundedCornerDecorProviderImpl(
+                    viewId = R.id.rounded_corner_top_left,
+                    alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP,
+                    alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT,
+                    roundedCornerResDelegate = roundedCornerResDelegate),
+                RoundedCornerDecorProviderImpl(
+                    viewId = R.id.rounded_corner_top_right,
+                    alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP,
+                    alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT,
+                    roundedCornerResDelegate = roundedCornerResDelegate),
+                RoundedCornerDecorProviderImpl(
+                    viewId = R.id.rounded_corner_bottom_left,
+                    alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM,
+                    alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT,
+                    roundedCornerResDelegate = roundedCornerResDelegate),
+                RoundedCornerDecorProviderImpl(
+                    viewId = R.id.rounded_corner_bottom_right,
+                    alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM,
+                    alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT,
+                    roundedCornerResDelegate = roundedCornerResDelegate)
+            )
+            hasTop -> listOf(
+                RoundedCornerDecorProviderImpl(
+                    viewId = R.id.rounded_corner_top_left,
+                    alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP,
+                    alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT,
+                    roundedCornerResDelegate = roundedCornerResDelegate),
+                RoundedCornerDecorProviderImpl(
+                    viewId = R.id.rounded_corner_top_right,
+                    alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP,
+                    alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT,
+                    roundedCornerResDelegate = roundedCornerResDelegate)
+            )
+            hasBottom -> listOf(
+                RoundedCornerDecorProviderImpl(
+                    viewId = R.id.rounded_corner_bottom_left,
+                    alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM,
+                    alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT,
+                    roundedCornerResDelegate = roundedCornerResDelegate),
+                RoundedCornerDecorProviderImpl(
+                    viewId = R.id.rounded_corner_bottom_right,
+                    alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM,
+                    alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT,
+                    roundedCornerResDelegate = roundedCornerResDelegate)
+            )
+            else -> emptyList()
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt
new file mode 100644
index 0000000..90ff950
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.decor
+
+import android.content.Context
+import android.view.DisplayCutout
+import android.view.Gravity
+import android.view.Surface
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.ImageView
+import com.android.systemui.R
+
+class RoundedCornerDecorProviderImpl(
+    override val viewId: Int,
+    @DisplayCutout.BoundsPosition override val alignedBound1: Int,
+    @DisplayCutout.BoundsPosition override val alignedBound2: Int,
+    private val roundedCornerResDelegate: RoundedCornerResDelegate
+) : CornerDecorProvider() {
+
+    private val isTop = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP)
+
+    override fun inflateView(
+        context: Context,
+        parent: ViewGroup,
+        @Surface.Rotation rotation: Int
+    ): View {
+        return ImageView(context).also { view ->
+            // View
+            view.id = viewId
+            initView(view, rotation)
+
+            // LayoutParams
+            val layoutSize = if (isTop) {
+                roundedCornerResDelegate.topRoundedSize
+            } else {
+                roundedCornerResDelegate.bottomRoundedSize
+            }
+            val params = FrameLayout.LayoutParams(
+                    layoutSize.width,
+                    layoutSize.height,
+                    alignedBound1.toLayoutGravity(rotation) or
+                            alignedBound2.toLayoutGravity(rotation))
+
+            // AddView
+            parent.addView(view, params)
+        }
+    }
+
+    private fun initView(view: ImageView, @Surface.Rotation rotation: Int) {
+        view.setRoundedCornerImage(roundedCornerResDelegate, isTop)
+        view.adjustRotation(alignedBounds, rotation)
+        view.setColorFilter(IMAGE_TINT_COLOR)
+    }
+
+    override fun onReloadResAndMeasure(
+        view: View,
+        reloadToken: Int,
+        @Surface.Rotation rotation: Int,
+        displayUniqueId: String?
+    ) {
+        roundedCornerResDelegate.updateDisplayUniqueId(displayUniqueId, reloadToken)
+
+        initView((view as ImageView), rotation)
+
+        val layoutSize = if (isTop) {
+            roundedCornerResDelegate.topRoundedSize
+        } else {
+            roundedCornerResDelegate.bottomRoundedSize
+        }
+        (view.layoutParams as FrameLayout.LayoutParams).let {
+            it.width = layoutSize.width
+            it.height = layoutSize.height
+            it.gravity = alignedBound1.toLayoutGravity(rotation) or
+                    alignedBound2.toLayoutGravity(rotation)
+            view.setLayoutParams(it)
+        }
+    }
+}
+
+private const val IMAGE_TINT_COLOR: Int = 0xFF000000.toInt()
+
+@DisplayCutout.BoundsPosition
+private fun Int.toLayoutGravity(@Surface.Rotation rotation: Int): Int = when (rotation) {
+    Surface.ROTATION_0 -> when (this) {
+        DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.LEFT
+        DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.TOP
+        DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.RIGHT
+        else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.BOTTOM
+    }
+    Surface.ROTATION_90 -> when (this) {
+        DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.BOTTOM
+        DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.LEFT
+        DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.TOP
+        else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.LEFT
+    }
+    Surface.ROTATION_270 -> when (this) {
+        DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.TOP
+        DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.RIGHT
+        DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.BOTTOM
+        else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.LEFT
+    }
+    else /* Surface.ROTATION_180 */ -> when (this) {
+        DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.RIGHT
+        DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.BOTTOM
+        DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.LEFT
+        else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.TOP
+    }
+}
+
+private fun ImageView.setRoundedCornerImage(
+    resDelegate: RoundedCornerResDelegate,
+    isTop: Boolean
+) {
+    val drawable = if (isTop)
+        resDelegate.topRoundedDrawable
+    else
+        resDelegate.bottomRoundedDrawable
+
+    if (drawable != null) {
+        setImageDrawable(drawable)
+    } else {
+        setImageResource(
+                if (isTop)
+                    R.drawable.rounded_corner_top
+                else
+                    R.drawable.rounded_corner_bottom
+        )
+    }
+}
+
+/**
+ * Configures the rounded corner drawable's view matrix based on the gravity.
+ *
+ * The gravity describes which corner to configure for, and the drawable we are rotating is assumed
+ * to be oriented for the top-left corner of the device regardless of the target corner.
+ * Therefore we need to rotate 180 degrees to get a bottom-left corner, and mirror in the x- or
+ * y-axis for the top-right and bottom-left corners.
+ */
+private fun ImageView.adjustRotation(alignedBounds: List<Int>, @Surface.Rotation rotation: Int) {
+    var newRotation = 0F
+    var newScaleX = 1F
+    var newScaleY = 1F
+
+    val isTop = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP)
+    val isLeft = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT)
+    when (rotation) {
+        Surface.ROTATION_0 -> when {
+            isTop && isLeft -> {}
+            isTop && !isLeft -> { newScaleX = -1F }
+            !isTop && isLeft -> { newScaleY = -1F }
+            else /* !isTop && !isLeft */ -> { newRotation = 180F }
+        }
+        Surface.ROTATION_90 -> when {
+            isTop && isLeft -> { newScaleY = -1F }
+            isTop && !isLeft -> {}
+            !isTop && isLeft -> { newRotation = 180F }
+            else /* !isTop && !isLeft */ -> { newScaleX = -1F }
+        }
+        Surface.ROTATION_270 -> when {
+            isTop && isLeft -> { newScaleX = -1F }
+            isTop && !isLeft -> { newRotation = 180F }
+            !isTop && isLeft -> {}
+            else /* !isTop && !isLeft */ -> { newScaleY = -1F }
+        }
+        else /* Surface.ROTATION_180 */ -> when {
+            isTop && isLeft -> { newRotation = 180F }
+            isTop && !isLeft -> { newScaleY = -1F }
+            !isTop && isLeft -> { newScaleX = -1F }
+            else /* !isTop && !isLeft */ -> {}
+        }
+    }
+
+    this.rotation = newRotation
+    this.scaleX = newScaleX
+    this.scaleY = newScaleY
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt
index ed175ef..c2bab26 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt
@@ -35,6 +35,8 @@
     private val density: Float
         get() = res.displayMetrics.density
 
+    private var reloadToken: Int = 0
+
     var isMultipleRadius: Boolean = false
         private set
 
@@ -59,12 +61,26 @@
         reloadMeasures()
     }
 
-    fun reloadAll(newDisplayUniqueId: String?) {
-        displayUniqueId = newDisplayUniqueId
+    private fun reloadAll(newReloadToken: Int) {
+        if (reloadToken == newReloadToken) {
+            return
+        }
+        reloadToken = newReloadToken
         reloadRes()
         reloadMeasures()
     }
 
+    fun updateDisplayUniqueId(newDisplayUniqueId: String?, newReloadToken: Int?) {
+        if (displayUniqueId != newDisplayUniqueId) {
+            displayUniqueId = newDisplayUniqueId
+            newReloadToken ?.let { reloadToken = it }
+            reloadRes()
+            reloadMeasures()
+        } else {
+            newReloadToken?.let { reloadAll(it) }
+        }
+    }
+
     private fun reloadRes() {
         val configIdx = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId)
         isMultipleRadius = getIsMultipleRadius(configIdx)
@@ -122,7 +138,11 @@
         }
     }
 
-    fun updateTuningSizeFactor(factor: Int) {
+    fun updateTuningSizeFactor(factor: Int?, newReloadToken: Int) {
+        if (reloadToken == newReloadToken) {
+            return
+        }
+        reloadToken = newReloadToken
         reloadMeasures(factor)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index fb1af8b..74949d0 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.dreams;
 
-import static com.android.keyguard.BouncerPanelExpansionCalculator.getBackScrimScaledExpansion;
+import static com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress;
 import static com.android.keyguard.BouncerPanelExpansionCalculator.getDreamAlphaScaledExpansion;
 import static com.android.keyguard.BouncerPanelExpansionCalculator.getDreamYPositionScaledExpansion;
 import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
@@ -217,19 +217,19 @@
 
         mBlurUtils.applyBlur(mView.getViewRootImpl(),
                 (int) mBlurUtils.blurRadiusOfRatio(
-                        1 - getBackScrimScaledExpansion(bouncerHideAmount)), false);
+                        1 - aboutToShowBouncerProgress(bouncerHideAmount)), false);
     }
 
     private static float getAlpha(int position, float expansion) {
         return Interpolators.LINEAR_OUT_SLOW_IN.getInterpolation(
                 position == POSITION_TOP ? getDreamAlphaScaledExpansion(expansion)
-                        : getBackScrimScaledExpansion(expansion + 0.03f));
+                        : aboutToShowBouncerProgress(expansion + 0.03f));
     }
 
     private float getTranslationY(int position, float expansion) {
         final float fraction = Interpolators.LINEAR_OUT_SLOW_IN.getInterpolation(
                 position == POSITION_TOP ? getDreamYPositionScaledExpansion(expansion)
-                        : getBackScrimScaledExpansion(expansion + 0.03f));
+                        : aboutToShowBouncerProgress(expansion + 0.03f));
         return MathUtils.lerp(-mDreamOverlayMaxTranslationY, 0, fraction);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 0819f30..44580aa 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -149,7 +149,7 @@
     /***************************************/
     // 900 - media
     public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, true);
-    public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, true);
+    public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, false);
     public static final BooleanFlag MEDIA_NEARBY_DEVICES = new BooleanFlag(903, true);
     public static final BooleanFlag MEDIA_MUTE_AWAIT = new BooleanFlag(904, true);
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 94b33e1..a8c2862 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -389,6 +389,10 @@
         listeners.add(listener)
     }
 
+    fun removeKeyguardUnlockAnimationListener(listener: KeyguardUnlockAnimationListener) {
+        listeners.remove(listener)
+    }
+
     /**
      * Called from [KeyguardViewMediator] to tell us that the RemoteAnimation on the surface behind
      * the keyguard has started successfully. We can use these parameters to directly manipulate the
diff --git a/packages/SystemUI/src/com/android/systemui/media/AnimationBindHandler.kt b/packages/SystemUI/src/com/android/systemui/media/AnimationBindHandler.kt
new file mode 100644
index 0000000..013683e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/AnimationBindHandler.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media
+
+import android.graphics.drawable.Animatable2
+import android.graphics.drawable.Drawable
+
+/**
+ * AnimationBindHandler is responsible for tracking the bound animation state and preventing
+ * jank and conflicts due to media notifications arriving at any time during an animation. It
+ * does this in two parts.
+ *  - Exit animations fired as a result of user input are tracked. When these are running, any
+ *      bind actions are delayed until the animation completes (and then fired in sequence).
+ *  - Continuous animations are tracked using their rebind id. Later calls using the same
+ *      rebind id will be totally ignored to prevent the continuous animation from restarting.
+ */
+internal class AnimationBindHandler : Animatable2.AnimationCallback() {
+    private val onAnimationsComplete = mutableListOf<() -> Unit>()
+    private val registrations = mutableListOf<Animatable2>()
+    private var rebindId: Int? = null
+
+    val isAnimationRunning: Boolean
+        get() = registrations.any { it.isRunning }
+
+    /**
+     * This check prevents rebinding to the action button if the identifier has not changed. A
+     * null value is always considered to be changed. This is used to prevent the connecting
+     * animation from rebinding (and restarting) if multiple buffer PlaybackStates are pushed by
+     * an application in a row.
+     */
+    fun updateRebindId(newRebindId: Int?): Boolean {
+        if (rebindId == null || newRebindId == null || rebindId != newRebindId) {
+            rebindId = newRebindId
+            return true
+        }
+        return false
+    }
+
+    fun tryRegister(drawable: Drawable?) {
+        if (drawable is Animatable2) {
+            val anim = drawable as Animatable2
+            anim.registerAnimationCallback(this)
+            registrations.add(anim)
+        }
+    }
+
+    fun unregisterAll() {
+        registrations.forEach { it.unregisterAnimationCallback(this) }
+        registrations.clear()
+    }
+
+    fun tryExecute(action: () -> Unit) {
+        if (isAnimationRunning) {
+            onAnimationsComplete.add(action)
+        } else {
+            action()
+        }
+    }
+
+    override fun onAnimationEnd(drawable: Drawable) {
+        super.onAnimationEnd(drawable)
+        if (!isAnimationRunning) {
+            onAnimationsComplete.forEach { it() }
+            onAnimationsComplete.clear()
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
new file mode 100644
index 0000000..8f0305f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media
+
+import android.animation.ArgbEvaluator
+import android.animation.ValueAnimator.AnimatorUpdateListener
+import android.animation.ValueAnimator
+import android.content.Context
+import android.content.res.ColorStateList
+import com.android.internal.R
+import com.android.internal.annotations.VisibleForTesting
+import com.android.settingslib.Utils
+import com.android.systemui.monet.ColorScheme
+
+/**
+ * ColorTransition is responsible for managing the animation between two specific colors.
+ * It uses a ValueAnimator to execute the animation and interpolate between the source color and
+ * the target color.
+ *
+ * Selection of the target color from the scheme, and application of the interpolated color
+ * are delegated to callbacks.
+ */
+open class ColorTransition(
+    private val defaultColor: Int,
+    private val extractColor: (ColorScheme) -> Int,
+    private val applyColor: (Int) -> Unit
+) : AnimatorUpdateListener {
+
+    private val argbEvaluator = ArgbEvaluator()
+    private val valueAnimator = buildAnimator()
+    var sourceColor: Int = defaultColor
+    var currentColor: Int = defaultColor
+    var targetColor: Int = defaultColor
+
+    override fun onAnimationUpdate(animation: ValueAnimator) {
+        currentColor = argbEvaluator.evaluate(
+            animation.animatedFraction, sourceColor, targetColor
+        ) as Int
+        applyColor(currentColor)
+    }
+
+    fun updateColorScheme(scheme: ColorScheme?) {
+        val newTargetColor = if (scheme == null) defaultColor else extractColor(scheme)
+        if (newTargetColor != targetColor) {
+            sourceColor = currentColor
+            targetColor = newTargetColor
+            valueAnimator.cancel()
+            valueAnimator.start()
+        }
+    }
+
+    init {
+        applyColor(defaultColor)
+    }
+
+    @VisibleForTesting
+    open fun buildAnimator(): ValueAnimator {
+        val animator = ValueAnimator.ofFloat(0f, 1f)
+        animator.duration = 333
+        animator.addUpdateListener(this)
+        return animator
+    }
+}
+
+typealias ColorTransitionFactory = (Int, (ColorScheme) -> Int, (Int) -> Unit) -> ColorTransition
+
+/**
+ * ColorSchemeTransition constructs a ColorTransition for each color in the scheme
+ * that needs to be transitioned when changed. It also sets up the assignment functions for sending
+ * the sending the interpolated colors to the appropriate views.
+ */
+class ColorSchemeTransition internal constructor(
+    private val context: Context,
+    bgColor: Int,
+    mediaViewHolder: MediaViewHolder,
+    colorTransitionFactory: ColorTransitionFactory
+) {
+    constructor(context: Context, bgColor: Int, mediaViewHolder: MediaViewHolder) :
+        this(context, bgColor, mediaViewHolder, ::ColorTransition)
+
+    val surfaceColor = colorTransitionFactory(
+        bgColor,
+        { colorScheme -> colorScheme.accent2[9] }, // A2-800
+        { surfaceColor ->
+            val colorList = ColorStateList.valueOf(surfaceColor)
+            mediaViewHolder.player.backgroundTintList = colorList
+            mediaViewHolder.albumView.foregroundTintList = colorList
+            mediaViewHolder.albumView.backgroundTintList = colorList
+            mediaViewHolder.seamlessIcon.imageTintList = colorList
+            mediaViewHolder.seamlessText.setTextColor(surfaceColor)
+            mediaViewHolder.dismissText.setTextColor(surfaceColor)
+        })
+
+    val accentPrimary = colorTransitionFactory(
+        loadDefaultColor(R.attr.textColorPrimary),
+        { colorScheme -> colorScheme.accent1[2] }, // A1-100
+        { accentPrimary ->
+            val accentColorList = ColorStateList.valueOf(accentPrimary)
+            mediaViewHolder.actionPlayPause.backgroundTintList = accentColorList
+            mediaViewHolder.seamlessButton.backgroundTintList = accentColorList
+            mediaViewHolder.settings.imageTintList = accentColorList
+            mediaViewHolder.cancelText.backgroundTintList = accentColorList
+            mediaViewHolder.dismissText.backgroundTintList = accentColorList
+        })
+
+    val textPrimary = colorTransitionFactory(
+        loadDefaultColor(R.attr.textColorPrimary),
+        { colorScheme -> colorScheme.neutral1[1] }, // N1-50
+        { textPrimary ->
+            mediaViewHolder.titleText.setTextColor(textPrimary)
+            val textColorList = ColorStateList.valueOf(textPrimary)
+            mediaViewHolder.seekBar.thumb.setTintList(textColorList)
+            mediaViewHolder.seekBar.progressTintList = textColorList
+            mediaViewHolder.longPressText.setTextColor(textColorList)
+            mediaViewHolder.cancelText.setTextColor(textColorList)
+            mediaViewHolder.scrubbingElapsedTimeView.setTextColor(textColorList)
+            mediaViewHolder.scrubbingTotalTimeView.setTextColor(textColorList)
+            for (button in mediaViewHolder.getTransparentActionButtons()) {
+                button.imageTintList = textColorList
+            }
+        })
+
+    val textPrimaryInverse = colorTransitionFactory(
+        loadDefaultColor(R.attr.textColorPrimaryInverse),
+        { colorScheme -> colorScheme.neutral1[10] }, // N1-900
+        { textPrimaryInverse ->
+            mediaViewHolder.actionPlayPause.imageTintList =
+                ColorStateList.valueOf(textPrimaryInverse)
+        })
+
+    val textSecondary = colorTransitionFactory(
+        loadDefaultColor(R.attr.textColorSecondary),
+        { colorScheme -> colorScheme.neutral2[3] }, // N2-200
+        { textSecondary -> mediaViewHolder.artistText.setTextColor(textSecondary) })
+
+    val textTertiary = colorTransitionFactory(
+        loadDefaultColor(R.attr.textColorTertiary),
+        { colorScheme -> colorScheme.neutral2[5] }, // N2-400
+        { textTertiary ->
+            mediaViewHolder.seekBar.progressBackgroundTintList =
+                ColorStateList.valueOf(textTertiary)
+        })
+
+    val colorTransitions = arrayOf(
+        surfaceColor, accentPrimary, textPrimary,
+        textPrimaryInverse, textSecondary, textTertiary)
+
+    private fun loadDefaultColor(id: Int): Int {
+        return Utils.getColorAttr(context, id).defaultColor
+    }
+
+    fun updateColorScheme(colorScheme: ColorScheme?) {
+        colorTransitions.forEach { it.updateColorScheme(colorScheme) }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index a86ed40..c9e888e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -18,6 +18,9 @@
 
 import static android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS;
 
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorSet;
 import android.app.PendingIntent;
 import android.app.WallpaperColors;
 import android.app.smartspace.SmartspaceAction;
@@ -31,20 +34,22 @@
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Rect;
 import android.graphics.drawable.Animatable;
-import android.graphics.drawable.Animatable2;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
+import android.graphics.drawable.TransitionDrawable;
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState;
 import android.os.Process;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.animation.Interpolator;
 import android.widget.ImageButton;
 import android.widget.ImageView;
-import android.widget.SeekBar;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
@@ -52,12 +57,14 @@
 import androidx.annotation.UiThread;
 import androidx.constraintlayout.widget.ConstraintSet;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.InstanceId;
 import com.android.settingslib.widget.AdaptiveIcon;
 import com.android.systemui.R;
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.animation.GhostedViewLaunchAnimatorController;
+import com.android.systemui.animation.Interpolators;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -148,6 +155,11 @@
     private MediaCarouselController mMediaCarouselController;
     private final MediaOutputDialogFactory mMediaOutputDialogFactory;
     private final FalsingManager mFalsingManager;
+    private MetadataAnimationHandler mMetadataAnimationHandler;
+    private ColorSchemeTransition mColorSchemeTransition;
+    private Drawable mPrevArtwork = null;
+    private int mArtworkBoundId = 0;
+    private int mArtworkNextBindRequestId = 0;
 
     // Used for logging.
     protected boolean mIsImpressed = false;
@@ -171,15 +183,20 @@
      * @param activityStarter    activity starter
      */
     @Inject
-    public MediaControlPanel(Context context,
+    public MediaControlPanel(
+            Context context,
             @Background Executor backgroundExecutor,
             @Main Executor mainExecutor,
-            ActivityStarter activityStarter, BroadcastSender broadcastSender,
-            MediaViewController mediaViewController, SeekBarViewModel seekBarViewModel,
+            ActivityStarter activityStarter,
+            BroadcastSender broadcastSender,
+            MediaViewController mediaViewController,
+            SeekBarViewModel seekBarViewModel,
             Lazy<MediaDataManager> lazyMediaDataManager,
             MediaOutputDialogFactory mediaOutputDialogFactory,
             MediaCarouselController mediaCarouselController,
-            FalsingManager falsingManager, SystemClock systemClock, MediaUiEventLogger logger) {
+            FalsingManager falsingManager,
+            SystemClock systemClock,
+            MediaUiEventLogger logger) {
         mContext = context;
         mBackgroundExecutor = backgroundExecutor;
         mMainExecutor = mainExecutor;
@@ -306,6 +323,33 @@
                 mActivityStarter.startActivity(SETTINGS_INTENT, true /* dismissShade */);
             }
         });
+
+        TextView titleText = mMediaViewHolder.getTitleText();
+        TextView artistText = mMediaViewHolder.getArtistText();
+        AnimatorSet enter = loadAnimator(R.anim.media_metadata_enter,
+                Interpolators.EMPHASIZED_DECELERATE, titleText, artistText);
+        AnimatorSet exit = loadAnimator(R.anim.media_metadata_exit,
+                Interpolators.EMPHASIZED_ACCELERATE, titleText, artistText);
+
+        mColorSchemeTransition = new ColorSchemeTransition(
+            mContext, mBackgroundColor, mMediaViewHolder);
+        mMetadataAnimationHandler = new MetadataAnimationHandler(exit, enter);
+    }
+
+    @VisibleForTesting
+    protected AnimatorSet loadAnimator(int animId, Interpolator motionInterpolator,
+            View... targets) {
+        ArrayList<Animator> animators = new ArrayList<>();
+        for (View target : targets) {
+            AnimatorSet animator = (AnimatorSet) AnimatorInflater.loadAnimator(mContext, animId);
+            animator.getChildAnimations().get(0).setInterpolator(motionInterpolator);
+            animator.setTarget(target);
+            animators.add(animator);
+        }
+
+        AnimatorSet result = new AnimatorSet();
+        result.playTogether(animators);
+        return result;
     }
 
     /** Attaches the recommendations to the recommendation view holder. */
@@ -377,20 +421,6 @@
             });
         }
 
-        // Accessibility label
-        mMediaViewHolder.getPlayer().setContentDescription(
-                mContext.getString(
-                        R.string.controls_media_playing_item_description,
-                        data.getSong(), data.getArtist(), data.getApp()));
-
-        // Song name
-        TextView titleText = mMediaViewHolder.getTitleText();
-        titleText.setText(data.getSong());
-
-        // Artist name
-        TextView artistText = mMediaViewHolder.getArtistText();
-        artistText.setText(data.getArtist());
-
         // Seek Bar
         final MediaController controller = getController();
         mBackgroundExecutor.execute(() -> mSeekBarViewModel.updateController(controller));
@@ -399,11 +429,16 @@
         bindLongPressMenu(data);
         bindActionButtons(data);
         bindScrubbingTime(data);
-        bindArtworkAndColors(data);
+
+        boolean isSongUpdated = bindSongMetadata(data);
+        bindArtworkAndColors(data, isSongUpdated);
 
         // TODO: We don't need to refresh this state constantly, only if the state actually changed
         // to something which might impact the measurement
-        mMediaViewController.refreshState();
+        // State refresh interferes with the translation animation, only run it if it's not running.
+        if (!mMetadataAnimationHandler.isRunning()) {
+            mMediaViewController.refreshState();
+        }
     }
 
     private void bindOutputSwitcherChip(MediaData data) {
@@ -494,120 +529,135 @@
         });
     }
 
-    private void bindArtworkAndColors(MediaData data) {
-        // Default colors
-        int surfaceColor = mBackgroundColor;
-        int accentPrimary = com.android.settingslib.Utils.getColorAttr(mContext,
-                com.android.internal.R.attr.textColorPrimary).getDefaultColor();
-        int textPrimary = com.android.settingslib.Utils.getColorAttr(mContext,
-                com.android.internal.R.attr.textColorPrimary).getDefaultColor();
-        int textPrimaryInverse = com.android.settingslib.Utils.getColorAttr(mContext,
-                com.android.internal.R.attr.textColorPrimaryInverse).getDefaultColor();
-        int textSecondary = com.android.settingslib.Utils.getColorAttr(mContext,
-                com.android.internal.R.attr.textColorSecondary).getDefaultColor();
-        int textTertiary = com.android.settingslib.Utils.getColorAttr(mContext,
-                com.android.internal.R.attr.textColorTertiary).getDefaultColor();
+    private boolean bindSongMetadata(MediaData data) {
+        // Accessibility label
+        mMediaViewHolder.getPlayer().setContentDescription(
+                mContext.getString(
+                        R.string.controls_media_playing_item_description,
+                        data.getSong(), data.getArtist(), data.getApp()));
 
-        // Album art
-        ColorScheme colorScheme = null;
-        ImageView albumView = mMediaViewHolder.getAlbumView();
-        boolean hasArtwork = data.getArtwork() != null;
-        if (hasArtwork) {
-            colorScheme = new ColorScheme(WallpaperColors.fromBitmap(data.getArtwork().getBitmap()),
-                    true);
+        TextView titleText = mMediaViewHolder.getTitleText();
+        TextView artistText = mMediaViewHolder.getArtistText();
+        return mMetadataAnimationHandler.setNext(
+            Pair.create(data.getSong(), data.getArtist()),
+            () -> {
+                titleText.setText(data.getSong());
+                artistText.setText(data.getArtist());
 
-            // Scale artwork to fit background
-            int width = mMediaViewHolder.getPlayer().getWidth();
-            int height = mMediaViewHolder.getPlayer().getHeight();
-            Drawable artwork = getScaledBackground(data.getArtwork(), width, height);
-            albumView.setPadding(0, 0, 0, 0);
-            albumView.setImageDrawable(artwork);
-            albumView.setClipToOutline(true);
-        } else {
-            // If there's no artwork, use colors from the app icon
-            try {
-                Drawable icon = mContext.getPackageManager().getApplicationIcon(
-                        data.getPackageName());
-                colorScheme = new ColorScheme(WallpaperColors.fromDrawable(icon), true);
-            } catch (PackageManager.NameNotFoundException e) {
-                Log.w(TAG, "Cannot find icon for package " + data.getPackageName(), e);
+                // refreshState is required here to resize the text views (and prevent ellipsis)
+                mMediaViewController.refreshState();
+
+                // Use OnPreDrawListeners to enforce zero alpha on these views for a frame.
+                // TransitionLayout insists on resetting the alpha of these views to 1 when onLayout
+                // is called which causes the animation to look bad. These suppress that behavior.
+                titleText.getViewTreeObserver().addOnPreDrawListener(
+                        new ViewTreeObserver.OnPreDrawListener() {
+                            @Override
+                            public boolean onPreDraw() {
+                                titleText.setAlpha(0);
+                                titleText.getViewTreeObserver().removeOnPreDrawListener(this);
+                                return true;
+                            }
+                        });
+
+                artistText.getViewTreeObserver().addOnPreDrawListener(
+                        new ViewTreeObserver.OnPreDrawListener() {
+                            @Override
+                            public boolean onPreDraw() {
+                                artistText.setAlpha(0);
+                                artistText.getViewTreeObserver().removeOnPreDrawListener(this);
+                                return true;
+                            }
+                        });
+
+                return Unit.INSTANCE;
+            },
+            () -> {
+                // After finishing the enter animation, we refresh state. This could pop if
+                // something is incorrectly bound, but needs to be run if other elements were
+                // updated while the enter animation was running
+                mMediaViewController.refreshState();
+                return Unit.INSTANCE;
+            });
+    }
+
+    private void bindArtworkAndColors(MediaData data, boolean updateBackground) {
+        final int reqId = mArtworkNextBindRequestId++;
+
+        // Capture width & height from views in foreground for artwork scaling in background
+        int width = mMediaViewHolder.getPlayer().getWidth();
+        int height = mMediaViewHolder.getPlayer().getHeight();
+
+        // WallpaperColors.fromBitmap takes a good amount of time. We do that work
+        // on the background executor to avoid stalling animations on the UI Thread.
+        mBackgroundExecutor.execute(() -> {
+            // Album art
+            ColorScheme mutableColorScheme = null;
+            Drawable artwork;
+            Icon artworkIcon = data.getArtwork();
+            if (artworkIcon != null) {
+                WallpaperColors wallpaperColors = WallpaperColors
+                        .fromBitmap(artworkIcon.getBitmap());
+                mutableColorScheme = new ColorScheme(wallpaperColors, true);
+                artwork = getScaledBackground(artworkIcon, width, height);
+            } else {
+                // If there's no artwork, use colors from the app icon
+                artwork = null;
+                try {
+                    Drawable icon = mContext.getPackageManager()
+                            .getApplicationIcon(data.getPackageName());
+                    mutableColorScheme = new ColorScheme(WallpaperColors.fromDrawable(icon), true);
+                } catch (PackageManager.NameNotFoundException e) {
+                    Log.w(TAG, "Cannot find icon for package " + data.getPackageName(), e);
+                }
             }
-        }
 
-        // Get colors for player
-        if (colorScheme != null) {
-            surfaceColor = colorScheme.getAccent2().get(9); // A2-800
-            accentPrimary = colorScheme.getAccent1().get(2); // A1-100
-            textPrimary = colorScheme.getNeutral1().get(1); // N1-50
-            textPrimaryInverse = colorScheme.getNeutral1().get(10); // N1-900
-            textSecondary = colorScheme.getNeutral2().get(3); // N2-200
-            textTertiary = colorScheme.getNeutral2().get(5); // N2-400
-        }
+            final ColorScheme colorScheme = mutableColorScheme;
+            mMainExecutor.execute(() -> {
+                // Cancel the request if a later one arrived first
+                if (reqId < mArtworkBoundId) return;
+                mArtworkBoundId = reqId;
 
-        ColorStateList bgColorList = ColorStateList.valueOf(surfaceColor);
-        ColorStateList accentColorList = ColorStateList.valueOf(accentPrimary);
-        ColorStateList textColorList = ColorStateList.valueOf(textPrimary);
+                // Bind the album view to the artwork or a transition drawable
+                ImageView albumView = mMediaViewHolder.getAlbumView();
+                albumView.setPadding(0, 0, 0, 0);
+                albumView.setClipToOutline(true);
+                if (updateBackground) {
+                    if (mPrevArtwork == null || artwork == null) {
+                        albumView.setImageDrawable(artwork);
+                    } else {
+                        TransitionDrawable transitionDrawable = new TransitionDrawable(
+                                new Drawable[] { mPrevArtwork, artwork });
+                        albumView.setImageDrawable(transitionDrawable);
+                        transitionDrawable.startTransition(333);
+                    }
+                    mPrevArtwork = artwork;
+                }
 
-        // Gradient and background (visible when there is no art)
-        albumView.setForegroundTintList(ColorStateList.valueOf(surfaceColor));
-        albumView.setBackgroundTintList(
-                ColorStateList.valueOf(surfaceColor));
-        mMediaViewHolder.getPlayer().setBackgroundTintList(bgColorList);
+                // Transition Colors to current color scheme
+                mColorSchemeTransition.updateColorScheme(colorScheme);
 
-        // App icon - use notification icon
-        ImageView appIconView = mMediaViewHolder.getAppIcon();
-        appIconView.clearColorFilter();
-        if (data.getAppIcon() != null && !data.getResumption()) {
-            appIconView.setImageIcon(data.getAppIcon());
-            appIconView.setColorFilter(accentPrimary);
-        } else {
-            // Resume players use launcher icon
-            appIconView.setColorFilter(getGrayscaleFilter());
-            try {
-                Drawable icon = mContext.getPackageManager().getApplicationIcon(
-                        data.getPackageName());
-                appIconView.setImageDrawable(icon);
-            } catch (PackageManager.NameNotFoundException e) {
-                Log.w(TAG, "Cannot find icon for package " + data.getPackageName(), e);
-                appIconView.setImageResource(R.drawable.ic_music_note);
-            }
-        }
-
-        // Metadata text
-        mMediaViewHolder.getTitleText().setTextColor(textPrimary);
-        mMediaViewHolder.getArtistText().setTextColor(textSecondary);
-
-        // Seekbar
-        SeekBar seekbar = mMediaViewHolder.getSeekBar();
-        seekbar.getThumb().setTintList(textColorList);
-        seekbar.setProgressTintList(textColorList);
-        seekbar.setProgressBackgroundTintList(ColorStateList.valueOf(textTertiary));
-        mMediaViewHolder.getScrubbingElapsedTimeView().setTextColor(textColorList);
-        mMediaViewHolder.getScrubbingTotalTimeView().setTextColor(textColorList);
-
-        // Action buttons
-        mMediaViewHolder.getActionPlayPause().setBackgroundTintList(accentColorList);
-        mMediaViewHolder.getActionPlayPause().setImageTintList(
-                ColorStateList.valueOf(textPrimaryInverse));
-        for (ImageButton button : mMediaViewHolder.getTransparentActionButtons()) {
-            button.setImageTintList(textColorList);
-        }
-
-        // Output switcher
-        View seamlessView = mMediaViewHolder.getSeamlessButton();
-        seamlessView.setBackgroundTintList(accentColorList);
-        ImageView seamlessIconView = mMediaViewHolder.getSeamlessIcon();
-        seamlessIconView.setImageTintList(bgColorList);
-        TextView seamlessText = mMediaViewHolder.getSeamlessText();
-        seamlessText.setTextColor(surfaceColor);
-
-        // Long press buttons
-        mMediaViewHolder.getLongPressText().setTextColor(textColorList);
-        mMediaViewHolder.getSettings().setImageTintList(accentColorList);
-        mMediaViewHolder.getCancelText().setTextColor(textColorList);
-        mMediaViewHolder.getCancelText().setBackgroundTintList(accentColorList);
-        mMediaViewHolder.getDismissText().setTextColor(surfaceColor);
-        mMediaViewHolder.getDismissText().setBackgroundTintList(accentColorList);
+                // App icon - use notification icon
+                ImageView appIconView = mMediaViewHolder.getAppIcon();
+                appIconView.clearColorFilter();
+                if (data.getAppIcon() != null && !data.getResumption()) {
+                    appIconView.setImageIcon(data.getAppIcon());
+                    appIconView.setColorFilter(
+                            mColorSchemeTransition.getAccentPrimary().getTargetColor());
+                } else {
+                    // Resume players use launcher icon
+                    appIconView.setColorFilter(getGrayscaleFilter());
+                    try {
+                        Drawable icon = mContext.getPackageManager()
+                                .getApplicationIcon(data.getPackageName());
+                        appIconView.setImageDrawable(icon);
+                    } catch (PackageManager.NameNotFoundException e) {
+                        Log.w(TAG, "Cannot find icon for package " + data.getPackageName(), e);
+                        appIconView.setImageResource(R.drawable.ic_music_note);
+                    }
+                }
+            });
+        });
     }
 
     private void bindActionButtons(MediaData data) {
@@ -714,6 +764,7 @@
         animHandler.tryExecute(() -> {
             bindButtonWithAnimations(button, mediaAction, animHandler);
             setSemanticButtonVisibleAndAlpha(button.getId(), mediaAction, semanticActions);
+            return Unit.INSTANCE;
         });
     }
 
@@ -802,11 +853,12 @@
     private void updateDisplayForScrubbingChange(@NonNull MediaButton semanticActions) {
         // Update visibilities of the scrubbing time views and the scrubbing-dependent buttons.
         bindScrubbingTime(mMediaData);
-        SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.forEach((id) ->
-                setSemanticButtonVisibleAndAlpha(
-                        id, semanticActions.getActionById(id), semanticActions));
-        // Trigger a state refresh so that we immediately update visibilities.
-        mMediaViewController.refreshState();
+        SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.forEach((id) -> setSemanticButtonVisibleAndAlpha(
+                id, semanticActions.getActionById(id), semanticActions));
+        if (!mMetadataAnimationHandler.isRunning()) {
+            // Trigger a state refresh so that we immediately update visibilities.
+            mMediaViewController.refreshState();
+        }
     }
 
     private void bindScrubbingTime(MediaData data) {
@@ -831,74 +883,6 @@
         );
     }
 
-    // AnimationBindHandler is responsible for tracking the bound animation state and preventing
-    // jank and conflicts due to media notifications arriving at any time during an animation. It
-    // does this in two parts.
-    //  - Exit animations fired as a result of user input are tracked. When these are running, any
-    //      bind actions are delayed until the animation completes (and then fired in sequence).
-    //  - Continuous animations are tracked using their rebind id. Later calls using the same
-    //      rebind id will be totally ignored to prevent the continuous animation from restarting.
-    private static class AnimationBindHandler extends Animatable2.AnimationCallback {
-        private ArrayList<Runnable> mOnAnimationsComplete = new ArrayList<>();
-        private ArrayList<Animatable2> mRegistrations = new ArrayList<>();
-        private Integer mRebindId = null;
-
-        // This check prevents rebinding to the action button if the identifier has not changed. A
-        // null value is always considered to be changed. This is used to prevent the connecting
-        // animation from rebinding (and restarting) if multiple buffer PlaybackStates are pushed by
-        // an application in a row.
-        public boolean updateRebindId(Integer rebindId) {
-            if (mRebindId == null || rebindId == null || !mRebindId.equals(rebindId)) {
-                mRebindId = rebindId;
-                return true;
-            }
-            return false;
-        }
-
-        public void tryRegister(Drawable drawable) {
-            if (drawable instanceof Animatable2) {
-                Animatable2 anim = (Animatable2) drawable;
-                anim.registerAnimationCallback(this);
-                mRegistrations.add(anim);
-            }
-        }
-
-        public void unregisterAll() {
-            for (Animatable2 anim : mRegistrations) {
-                anim.unregisterAnimationCallback(this);
-            }
-            mRegistrations.clear();
-        }
-
-        public boolean isAnimationRunning() {
-            for (Animatable2 anim : mRegistrations) {
-                if (anim.isRunning()) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        public void tryExecute(Runnable action) {
-            if (isAnimationRunning()) {
-                mOnAnimationsComplete.add(action);
-            } else {
-                action.run();
-            }
-        }
-
-        @Override
-        public void onAnimationEnd(Drawable drawable) {
-            super.onAnimationEnd(drawable);
-            if (!isAnimationRunning()) {
-                for (Runnable action : mOnAnimationsComplete) {
-                    action.run();
-                }
-                mOnAnimationsComplete.clear();
-            }
-        }
-    }
-
     @Nullable
     private ActivityLaunchAnimator.Controller buildLaunchAnimatorController(
             TransitionLayout player) {
@@ -1104,7 +1088,9 @@
         });
 
         mController = null;
-        mMediaViewController.refreshState();
+        if (mMetadataAnimationHandler == null || !mMetadataAnimationHandler.isRunning()) {
+            mMediaViewController.refreshState();
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/media/MetadataAnimationHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MetadataAnimationHandler.kt
new file mode 100644
index 0000000..9a1a6d3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/MetadataAnimationHandler.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
+import com.android.internal.annotations.VisibleForTesting
+
+/**
+ * MetadataAnimationHandler controls the current state of the MediaControlPanel's transition motion.
+ *
+ * It checks for a changed data object (artist & title from MediaControlPanel) and runs the
+ * animation if necessary. When the motion has fully transitioned the elements out, it runs the
+ * update callback to modify the view data, before the enter animation runs.
+ */
+internal open class MetadataAnimationHandler(
+    private val exitAnimator: Animator,
+    private val enterAnimator: Animator
+) : AnimatorListenerAdapter() {
+
+    private val animator: AnimatorSet
+    private var postExitUpdate: (() -> Unit)? = null
+    private var postEnterUpdate: (() -> Unit)? = null
+    private var targetData: Any? = null
+
+    val isRunning: Boolean
+        get() = animator.isRunning
+
+    fun setNext(targetData: Any, postExit: () -> Unit, postEnter: () -> Unit): Boolean {
+        if (targetData != this.targetData) {
+            this.targetData = targetData
+            postExitUpdate = postExit
+            postEnterUpdate = postEnter
+            if (!animator.isRunning) {
+                animator.start()
+            }
+            return true
+        }
+        return false
+    }
+
+    override fun onAnimationEnd(animator: Animator) {
+        if (animator === exitAnimator) {
+            postExitUpdate?.let { it() }
+            postExitUpdate = null
+        }
+
+        if (animator === enterAnimator) {
+            // Another new update appeared while entering
+            if (postExitUpdate != null) {
+                this.animator.start()
+            } else {
+                postEnterUpdate?.let { it() }
+                postEnterUpdate = null
+            }
+        }
+    }
+
+    init {
+        exitAnimator.addListener(this)
+        enterAnimator.addListener(this)
+        animator = buildAnimatorSet(exitAnimator, enterAnimator)
+    }
+
+    @VisibleForTesting
+    protected open fun buildAnimatorSet(exit: Animator, enter: Animator): AnimatorSet {
+        val result = AnimatorSet()
+        result.playSequentially(exitAnimator, enterAnimator)
+        return result
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
index 612a7f9..c9d300b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
@@ -16,20 +16,29 @@
 
 package com.android.systemui.media
 
+import android.animation.Animator
+import android.animation.ObjectAnimator
 import android.text.format.DateUtils
 import androidx.annotation.UiThread
 import androidx.lifecycle.Observer
+import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.R
+import com.android.systemui.animation.Interpolators
 
 /**
  * Observer for changes from SeekBarViewModel.
  *
  * <p>Updates the seek bar views in response to changes to the model.
  */
-class SeekBarObserver(
+open class SeekBarObserver(
     private val holder: MediaViewHolder
 ) : Observer<SeekBarViewModel.Progress> {
 
+    companion object {
+        @JvmStatic val RESET_ANIMATION_DURATION_MS: Int = 750
+        @JvmStatic val RESET_ANIMATION_THRESHOLD_MS: Int = 250
+    }
+
     val seekBarEnabledMaxHeight = holder.seekBar.context.resources
         .getDimensionPixelSize(R.dimen.qs_media_enabled_seekbar_height)
     val seekBarDisabledHeight = holder.seekBar.context.resources
@@ -38,6 +47,7 @@
                 .getDimensionPixelSize(R.dimen.qs_media_session_enabled_seekbar_vertical_padding)
     val seekBarDisabledVerticalPadding = holder.seekBar.context.resources
                 .getDimensionPixelSize(R.dimen.qs_media_session_disabled_seekbar_vertical_padding)
+    var seekBarResetAnimator: Animator? = null
 
     init {
         val seekBarProgressWavelength = holder.seekBar.context.resources
@@ -91,7 +101,17 @@
         holder.scrubbingTotalTimeView.text = totalTimeString
 
         data.elapsedTime?.let {
-            holder.seekBar.setProgress(it)
+            if (!data.scrubbing && !(seekBarResetAnimator?.isRunning ?: false)) {
+                if (it <= RESET_ANIMATION_THRESHOLD_MS &&
+                        holder.seekBar.progress > RESET_ANIMATION_THRESHOLD_MS) {
+                    // This animation resets for every additional update to zero.
+                    val animator = buildResetAnimator(it)
+                    animator.start()
+                    seekBarResetAnimator = animator
+                } else {
+                    holder.seekBar.progress = it
+                }
+            }
             val elapsedTimeString = DateUtils.formatElapsedTime(
                 it / DateUtils.SECOND_IN_MILLIS)
             holder.scrubbingElapsedTimeView.text = elapsedTimeString
@@ -104,6 +124,16 @@
         }
     }
 
+    @VisibleForTesting
+    open fun buildResetAnimator(targetTime: Int): Animator {
+        val animator = ObjectAnimator.ofInt(holder.seekBar, "progress",
+                holder.seekBar.progress, targetTime + RESET_ANIMATION_DURATION_MS)
+        animator.setAutoCancel(true)
+        animator.duration = RESET_ANIMATION_DURATION_MS.toLong()
+        animator.interpolator = Interpolators.EMPHASIZED
+        return animator
+    }
+
     @UiThread
     fun setVerticalPadding(padding: Int) {
         val leftPadding = holder.seekBar.paddingLeft
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 0f45a75..740ecff 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -83,6 +83,7 @@
 import android.view.Display;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
+import android.view.InsetsState;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.InsetsVisibilities;
 import android.view.KeyEvent;
@@ -196,6 +197,7 @@
     private final Optional<Pip> mPipOptional;
     private final Optional<Recents> mRecentsOptional;
     private final DeviceConfigProxy mDeviceConfigProxy;
+    private final NavigationBarTransitions mNavigationBarTransitions;
     private final Optional<BackAnimation> mBackAnimation;
     private final Handler mHandler;
     private final NavigationBarOverlayController mNavbarOverlayController;
@@ -512,6 +514,7 @@
             InputMethodManager inputMethodManager,
             DeadZone deadZone,
             DeviceConfigProxy deviceConfigProxy,
+            NavigationBarTransitions navigationBarTransitions,
             Optional<BackAnimation> backAnimation) {
         super(navigationBarView);
         mFrame = navigationBarFrame;
@@ -536,6 +539,7 @@
         mRecentsOptional = recentsOptional;
         mDeadZone = deadZone;
         mDeviceConfigProxy = deviceConfigProxy;
+        mNavigationBarTransitions = navigationBarTransitions;
         mBackAnimation = backAnimation;
         mHandler = mainHandler;
         mNavbarOverlayController = navbarOverlayController;
@@ -560,6 +564,7 @@
     public void onInit() {
         // TODO: A great deal of this code should probably live in onViewAttached.
         // It should also has corresponding cleanup in onViewDetached.
+        mView.setBarTransitions(mNavigationBarTransitions);
         mView.setTouchHandler(mTouchHandler);
         mView.setNavBarMode(mNavBarMode);
         mView.updateRotationButton();
@@ -631,7 +636,7 @@
         mView.setOnVerticalChangedListener(this::onVerticalChanged);
         mView.setOnTouchListener(this::onNavigationTouch);
         if (mSavedState != null) {
-            mView.getLightTransitionsController().restoreState(mSavedState);
+            getBarTransitions().getLightTransitionsController().restoreState(mSavedState);
         }
         setNavigationIconHints(mNavigationIconHints);
         mView.setWindowVisible(isNavBarWindowVisible());
@@ -704,8 +709,7 @@
                 mView.getRotationButtonController();
         rotationButtonController.setRotationCallback(null);
         mView.setUpdateActiveTouchRegionsCallback(null);
-        mView.getBarTransitions().destroy();
-        mView.getLightTransitionsController().destroy(mContext);
+        getBarTransitions().destroy();
         mOverviewProxyService.removeCallback(mOverviewProxyListener);
         mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
         if (mOrientationHandle != null) {
@@ -731,7 +735,7 @@
         outState.putInt(EXTRA_APPEARANCE, mAppearance);
         outState.putInt(EXTRA_BEHAVIOR, mBehavior);
         outState.putBoolean(EXTRA_TRANSIENT_STATE, mTransientShown);
-        mView.getLightTransitionsController().saveState(outState);
+        getBarTransitions().getLightTransitionsController().saveState(outState);
     }
 
     /**
@@ -892,7 +896,7 @@
         pw.println("  mTransientShown=" + mTransientShown);
         pw.println("  mTransientShownFromGestureOnSystemBar="
                 + mTransientShownFromGestureOnSystemBar);
-        dumpBarTransitions(pw, "mNavigationBarView", mView.getBarTransitions());
+        dumpBarTransitions(pw, "mNavigationBarView", getBarTransitions());
         mView.dump(pw);
     }
 
@@ -1429,7 +1433,7 @@
         mLightBarController = lightBarController;
         if (mLightBarController != null) {
             mLightBarController.setNavigationBar(
-                    mView.getLightTransitionsController());
+                    getBarTransitions().getLightTransitionsController());
         }
     }
 
@@ -1471,7 +1475,7 @@
                 mCentralSurfacesOptionalLazy.get().map(CentralSurfaces::isDeviceInteractive)
                         .orElse(false)
                 && mNavigationBarWindowState != WINDOW_STATE_HIDDEN;
-        mView.getBarTransitions().transitionTo(mTransitionMode, anim);
+        getBarTransitions().transitionTo(mTransitionMode, anim);
     }
 
     public void disableAnimationsDuringHide(long delay) {
@@ -1491,11 +1495,11 @@
     }
 
     public NavigationBarTransitions getBarTransitions() {
-        return mView.getBarTransitions();
+        return mNavigationBarTransitions;
     }
 
     public void finishBarAnimations() {
-        mView.getBarTransitions().finishAnimations();
+        getBarTransitions().finishAnimations();
     }
 
     private WindowManager.LayoutParams getBarLayoutParams(int rotation) {
@@ -1558,10 +1562,12 @@
                         | WindowManager.LayoutParams.FLAG_SLIPPERY,
                 PixelFormat.TRANSLUCENT);
         lp.gravity = gravity;
+        lp.providedInternalInsets = new Insets[InsetsState.SIZE];
         if (insetsHeight != -1) {
-            lp.providedInternalInsets = Insets.of(0, height - insetsHeight, 0, 0);
+            lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] =
+                    Insets.of(0, height - insetsHeight, 0, 0);
         } else {
-            lp.providedInternalInsets = Insets.NONE;
+            lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] = null;
         }
         lp.token = new Binder();
         lp.accessibilityTitle = mContext.getString(R.string.nav_bar);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
index 58e07db..e625501 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
@@ -31,17 +31,19 @@
 
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
 import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.BarTransitions;
 import com.android.systemui.statusbar.phone.LightBarTransitionsController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.inject.Inject;
+
+/** */
+@NavigationBarScope
 public final class NavigationBarTransitions extends BarTransitions implements
         LightBarTransitionsController.DarkIntensityApplier {
 
@@ -81,15 +83,13 @@
         }
     };
 
-    public NavigationBarTransitions(NavigationBarView view, CommandQueue commandQueue) {
+    @Inject
+    public NavigationBarTransitions(
+            NavigationBarView view,
+            LightBarTransitionsController.Factory lightBarTransitionsControllerFactory) {
         super(view, R.drawable.nav_background);
         mView = view;
-        mLightTransitionsController = new LightBarTransitionsController(
-                view.getContext(),
-                this,
-                commandQueue,
-                Dependency.get(KeyguardStateController.class),
-                Dependency.get(StatusBarStateController.class));
+        mLightTransitionsController = lightBarTransitionsControllerFactory.create(this);
         mAllowAutoDimWallpaperNotVisible = view.getContext().getResources()
                 .getBoolean(R.bool.config_navigation_bar_enable_auto_dim_no_visible_wallpaper);
         mDarkIntensityListeners = new ArrayList();
@@ -127,6 +127,7 @@
                     Display.DEFAULT_DISPLAY);
         } catch (RemoteException e) {
         }
+        mLightTransitionsController.destroy();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index abff914..8878c2d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -85,7 +85,6 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.phone.LightBarTransitionsController;
@@ -140,7 +139,7 @@
     private EdgeBackGestureHandler mEdgeBackGestureHandler;
     private final DeadZone mDeadZone;
     private boolean mDeadZoneConsuming = false;
-    private final NavigationBarTransitions mBarTransitions;
+    private NavigationBarTransitions mBarTransitions;
     @Nullable
     private AutoHideController mAutoHideController;
 
@@ -370,7 +369,6 @@
         mConfiguration.updateFrom(context.getResources().getConfiguration());
 
         mScreenPinningNotify = new ScreenPinningNotify(mContext);
-        mBarTransitions = new NavigationBarTransitions(this, Dependency.get(CommandQueue.class));
 
         mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back));
         mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home));
@@ -418,12 +416,12 @@
         }
     }
 
-    public void setAutoHideController(AutoHideController autoHideController) {
-        mAutoHideController = autoHideController;
+    void setBarTransitions(NavigationBarTransitions navigationBarTransitions) {
+        mBarTransitions = navigationBarTransitions;
     }
 
-    public NavigationBarTransitions getBarTransitions() {
-        return mBarTransitions;
+    public void setAutoHideController(AutoHideController autoHideController) {
+        mAutoHideController = autoHideController;
     }
 
     public LightBarTransitionsController getLightTransitionsController() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index cdc6b3b..363baaa 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -264,7 +264,7 @@
             mWindowContext = null;
         }
         mAutoHideController.setNavigationBar(null);
-        mLightBarTransitionsController.destroy(mContext);
+        mLightBarTransitionsController.destroy();
         mLightBarController.setNavigationBar(null);
         mPipOptional.ifPresent(this::removePipExclusionBoundsChangeListener);
         mInitialized = false;
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 097cf35..00aa138 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -706,7 +706,7 @@
                 Drawable drawable = resolveImage(imageUri, mContext);
                 Bitmap bitmap = convertDrawableToBitmap(drawable);
                 views.setImageViewBitmap(R.id.image, bitmap);
-            } catch (IOException e) {
+            } catch (IOException | SecurityException e) {
                 Log.e(TAG, "Could not decode image: " + e);
                 // If we couldn't load the image, show text that we have a new image.
                 views.setTextViewText(R.id.text_content, newImageDescription);
@@ -754,7 +754,7 @@
         return views;
     }
 
-    private Drawable resolveImage(Uri uri, Context context) throws IOException {
+     Drawable resolveImage(Uri uri, Context context) throws IOException {
         final ImageDecoder.Source source =
                 ImageDecoder.createSource(context.getContentResolver(), uri);
         final Drawable drawable =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index f87f81e..76ec6bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -519,7 +519,10 @@
     @Override
     public void setOverScrollAmount(int overScrollAmount) {
         mOverScrolling = overScrollAmount != 0;
-        getView().setTranslationY(overScrollAmount);
+        View view = getView();
+        if (view != null) {
+            view.setTranslationY(overScrollAmount);
+        }
     }
 
     @Override
@@ -610,7 +613,7 @@
             view.setVisibility((View.VISIBLE));
         }
         float alpha = mQSPanelController.bouncerInTransit()
-                ? BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(progress)
+                ? BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(progress)
                 : ShadeInterpolation.getContentAlpha(progress);
         view.setAlpha(alpha);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 9fbdd3c..9de132f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -364,7 +364,7 @@
         setPaddingRelative(getPaddingStart(),
                 paddingTop,
                 getPaddingEnd(),
-                getPaddingEnd());
+                getPaddingBottom());
     }
 
     void addOnConfigurationChangedListener(OnConfigurationChangedListener listener) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 7e0410c..dd99db4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -143,9 +143,10 @@
 
     @Inject
     QSSecurityFooter(@Named(QS_SECURITY_FOOTER_VIEW) View rootView,
-            UserTracker userTracker, @Main Handler mainHandler, ActivityStarter activityStarter,
-            SecurityController securityController, DialogLaunchAnimator dialogLaunchAnimator,
-            @Background Looper bgLooper, BroadcastDispatcher broadcastDispatcher) {
+            UserTracker userTracker, @Main Handler mainHandler,
+            ActivityStarter activityStarter, SecurityController securityController,
+            DialogLaunchAnimator dialogLaunchAnimator, @Background Looper bgLooper,
+            BroadcastDispatcher broadcastDispatcher) {
         super(rootView);
         mFooterText = mView.findViewById(R.id.footer_text);
         mPrimaryFooterIcon = mView.findViewById(R.id.primary_footer_icon);
@@ -493,21 +494,23 @@
 
     private void createDialog() {
         mShouldUseSettingsButton.set(false);
-        final View view = createDialogView();
-        mMainHandler.post(() -> {
-            mDialog = new SystemUIDialog(mContext, 0); // Use mContext theme
-            mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
-            mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this);
-            mDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
-                    mShouldUseSettingsButton.get() ? getSettingsButton() : getNegativeButton(),
-                    this);
+        mHandler.post(() -> {
+            String settingsButtonText = getSettingsButton();
+            final View view = createDialogView();
+            mMainHandler.post(() -> {
+                mDialog = new SystemUIDialog(mContext, 0); // Use mContext theme
+                mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+                mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this);
+                mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, mShouldUseSettingsButton.get()
+                        ? settingsButtonText : getNegativeButton(), this);
 
-            mDialog.setView(view);
-            if (mView.isAggregatedVisible()) {
-                mDialogLaunchAnimator.showFromView(mDialog, mView);
-            } else {
-                mDialog.show();
-            }
+                mDialog.setView(view);
+                if (mView.isAggregatedVisible()) {
+                    mDialogLaunchAnimator.showFromView(mDialog, mView);
+                } else {
+                    mDialog.show();
+                }
+            });
         });
     }
 
@@ -650,6 +653,7 @@
         }
     }
 
+    // This should not be called on the main thread to avoid making an IPC.
     @VisibleForTesting
     String getSettingsButton() {
         return mDpm.getResources().getString(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 3c95da8..112b1e8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -71,7 +71,11 @@
 
     @Override
     protected void updatePadding() {
-        // QS Panel is setting a top padding by default, which we don't need.
+        int bottomPadding = getResources().getDimensionPixelSize(R.dimen.qqs_layout_padding_bottom);
+        setPaddingRelative(getPaddingStart(),
+                getPaddingTop(),
+                getPaddingEnd(),
+                bottomPadding);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 9a932ba..270bdc7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -11,6 +11,7 @@
 import android.graphics.PorterDuffXfermode
 import android.graphics.RadialGradient
 import android.graphics.Shader
+import android.os.Trace
 import android.util.AttributeSet
 import android.util.MathUtils.lerp
 import android.view.View
@@ -222,6 +223,8 @@
 
                 revealEffect.setRevealAmountOnScrim(value, this)
                 updateScrimOpaque()
+                Trace.traceCounter(Trace.TRACE_TAG_APP, "light_reveal_amount",
+                        (field * 100).toInt())
                 invalidate()
             }
         }
@@ -355,8 +358,8 @@
     }
 
     override fun onDraw(canvas: Canvas?) {
-        if (canvas == null || revealGradientWidth <= 0 || revealGradientHeight <= 0
-            || revealAmount == 0f) {
+        if (canvas == null || revealGradientWidth <= 0 || revealGradientHeight <= 0 ||
+            revealAmount == 0f) {
             if (revealAmount < 1f) {
                 canvas?.drawColor(revealGradientEndColor)
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 5bf75c7..efb46b96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -32,6 +32,7 @@
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
+import android.app.role.RoleManager;
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -380,12 +381,22 @@
                 Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 0, USER_SYSTEM) == 1) {
             INotificationManager iNm = INotificationManager.Stub.asInterface(
                     ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+
+            boolean isSystem = false;
             try {
-                return iNm.isPermissionFixed(sbn.getPackageName(), sbn.getUserId());
+                isSystem = iNm.isPermissionFixed(sbn.getPackageName(), sbn.getUserId());
             } catch (RemoteException e) {
                 Log.e(TAG, "cannot reach NMS");
             }
-            return false;
+            RoleManager rm = context.getSystemService(RoleManager.class);
+            List<String> fixedRoleHolders = new ArrayList<>();
+            fixedRoleHolders.addAll(rm.getRoleHolders(RoleManager.ROLE_DIALER));
+            fixedRoleHolders.addAll(rm.getRoleHolders(RoleManager.ROLE_EMERGENCY));
+            if (fixedRoleHolders.contains(sbn.getPackageName())) {
+                isSystem = true;
+            }
+
+            return isSystem;
         } else {
             PackageManager packageManager = CentralSurfaces.getPackageManagerForUser(
                     context, sbn.getUser().getIdentifier());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 59a78ed..8b01a47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -124,6 +124,7 @@
     private NotificationEntry mEntry;
     private StatusBarNotification mSbn;
     private boolean mIsDeviceProvisioned;
+    private boolean mIsSystemRegisteredCall;
 
     private OnSettingsClickListener mOnSettingsClickListener;
     private OnAppSettingsClickListener mAppSettingsClickListener;
@@ -229,6 +230,9 @@
         mShowAutomaticSetting = mAssistantFeedbackController.isFeedbackEnabled();
         mUiEventLogger = uiEventLogger;
 
+        mIsSystemRegisteredCall = mSbn.getNotification().isStyle(Notification.CallStyle.class)
+                && mINotificationManager.isInCall(mSbn.getPackageName(), mSbn.getUid());
+
         int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
                 pkg, mAppUid, false /* includeDeleted */);
         if (mNumUniqueChannelsInRow == 0) {
@@ -252,17 +256,27 @@
     }
 
     private void bindInlineControls() {
-        if (mIsNonblockable) {
+        if (mIsSystemRegisteredCall) {
+            findViewById(R.id.non_configurable_call_text).setVisibility(VISIBLE);
+            findViewById(R.id.non_configurable_text).setVisibility(GONE);
+            findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE);
+            findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
+            ((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button);
+            findViewById(R.id.turn_off_notifications).setVisibility(GONE);
+        } else if (mIsNonblockable) {
             findViewById(R.id.non_configurable_text).setVisibility(VISIBLE);
+            findViewById(R.id.non_configurable_call_text).setVisibility(GONE);
             findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE);
             findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
             ((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button);
             findViewById(R.id.turn_off_notifications).setVisibility(GONE);
         } else if (mNumUniqueChannelsInRow > 1) {
+            findViewById(R.id.non_configurable_call_text).setVisibility(GONE);
             findViewById(R.id.non_configurable_text).setVisibility(GONE);
             findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
             findViewById(R.id.non_configurable_multichannel_text).setVisibility(VISIBLE);
         } else {
+            findViewById(R.id.non_configurable_call_text).setVisibility(GONE);
             findViewById(R.id.non_configurable_text).setVisibility(GONE);
             findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE);
             findViewById(R.id.interruptiveness_settings).setVisibility(VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 15a93c8..b52fd61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -417,7 +417,6 @@
     private NotificationShelf mShelf;
     private int mMaxDisplayedNotifications = -1;
     private float mKeyguardBottomPadding = -1;
-    private float mKeyguardNotificationAvailableSpace = -1;
     @VisibleForTesting int mStatusBarHeight;
     private int mMinInteractionHeight;
     private final Rect mClipRect = new Rect();
@@ -775,9 +774,11 @@
         y = (int) mMaxLayoutHeight;
         drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "mMaxLayoutHeight = " + y);
 
+        // The space between mTopPadding and mKeyguardBottomPadding determines the available space
+        // for notifications on keyguard.
         if (mKeyguardBottomPadding >= 0) {
             y = getHeight() - (int) mKeyguardBottomPadding;
-            drawDebugInfo(canvas, y, Color.GRAY,
+            drawDebugInfo(canvas, y, Color.RED,
                     /* label= */ "getHeight() - mKeyguardBottomPadding = " + y);
         }
 
@@ -789,7 +790,7 @@
         drawDebugInfo(canvas, y, Color.CYAN, /* label= */ "mAmbientState.getStackY() = " + y);
 
         y = (int) (mAmbientState.getStackY() + mAmbientState.getStackHeight());
-        drawDebugInfo(canvas, y, Color.BLUE,
+        drawDebugInfo(canvas, y, Color.LTGRAY,
                 /* label= */ "mAmbientState.getStackY() + mAmbientState.getStackHeight() = " + y);
 
         y = (int) mAmbientState.getStackY() + mContentHeight;
@@ -800,10 +801,6 @@
         drawDebugInfo(canvas, y, Color.YELLOW,
                 /* label= */ "mAmbientState.getStackY() + mIntrinsicContentHeight = " + y);
 
-        y = (int) (mAmbientState.getStackY() + mKeyguardNotificationAvailableSpace);
-        drawDebugInfo(canvas, y, Color.RED, /* label= */
-                "mAmbientState.getStackY() + mKeyguardNotificationAvailableSpace = " + y);
-
         drawDebugInfo(canvas, mRoundedRectClippingBottom, Color.DKGRAY,
                 /* label= */ "mRoundedRectClippingBottom) = " + y);
     }
@@ -2267,10 +2264,11 @@
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void updateContentHeight() {
         final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mMinimumPaddings;
+        final int shelfIntrinsicHeight = mShelf != null ? mShelf.getIntrinsicHeight() : 0;
         final int height =
                 (int) scrimTopPadding + (int) mNotificationStackSizeCalculator.computeHeight(
                         /* notificationStackScrollLayout= */ this, mMaxDisplayedNotifications,
-                        mShelf != null ? mShelf.getIntrinsicHeight() : 0);
+                        shelfIntrinsicHeight);
         mIntrinsicContentHeight = height;
 
         // The topPadding can be bigger than the regular padding when qs is expanded, in that
@@ -4914,15 +4912,6 @@
         mKeyguardBottomPadding = keyguardBottomPadding;
     }
 
-    /**
-     * For debugging only. Enables to draw a line related to the available size for notifications in
-     * keyguard.
-     */
-    public void setKeyguardAvailableSpaceForDebug(float keyguardNotificationAvailableSpace) {
-        mKeyguardNotificationAvailableSpace = keyguardNotificationAvailableSpace;
-    }
-
-
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
         mShouldShowShelfOnly = shouldShowShelfOnly;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 440bea6..ea6987a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -1305,12 +1305,6 @@
         mView.setKeyguardBottomPadding(keyguardBottomPadding);
     }
 
-    /** For debugging only. */
-    public void mKeyguardNotificationAvailableSpaceForDebug(
-            float keyguardNotificationAvailableSpace) {
-        mView.setKeyguardAvailableSpaceForDebug(keyguardNotificationAvailableSpace);
-    }
-
     public RemoteInputController.Delegate createDelegate() {
         return new RemoteInputController.Delegate() {
             public void setRemoteInputActive(NotificationEntry entry,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index 7fb115d..9417aac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.util.children
 import javax.inject.Inject
 import kotlin.math.max
+import kotlin.math.min
 import kotlin.properties.Delegates.notNull
 
 private const val TAG = "NotificationStackSizeCalculator"
@@ -51,9 +52,7 @@
      */
     private var maxKeyguardNotifications by notNull<Int>()
 
-    /**
-     * Minimum space between two notifications, see [calculateGapAndDividerHeight].
-     */
+    /** Minimum space between two notifications, see [calculateGapAndDividerHeight]. */
     private var dividerHeight by notNull<Int>()
 
     init {
@@ -61,55 +60,34 @@
     }
 
     /**
-     * Given the [availableSpace] constraint, calculates how many notification to show.
+     * Given the [totalAvailableSpace] constraint, calculates how many notification to show.
      *
      * This number is only valid in keyguard.
      *
-     * @param availableSpace space for notifications. This doesn't include the space for the shelf.
+     * @param totalAvailableSpace space for notifications. This includes the space for the shelf.
      */
     fun computeMaxKeyguardNotifications(
         stack: NotificationStackScrollLayout,
-        availableSpace: Float,
-        shelfHeight: Float
+        totalAvailableSpace: Float,
+        shelfIntrinsicHeight: Float
     ): Int {
+        val stackHeightSequence = computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight)
+
+        var maxNotifications =
+            stackHeightSequence.lastIndexWhile { stackHeight -> stackHeight <= totalAvailableSpace }
+
+        if (onLockscreen()) {
+            maxNotifications = min(maxKeyguardNotifications, maxNotifications)
+        }
+
+        // Could be < 0 if the space available is less than the shelf size. Returns 0 in this case.
+        maxNotifications = max(0, maxNotifications)
         log {
             "computeMaxKeyguardNotifications(" +
-                "availableSpace=$availableSpace shelfHeight=$shelfHeight)"
+                "availableSpace=$totalAvailableSpace" +
+                " shelfHeight=$shelfIntrinsicHeight) -> $maxNotifications"
         }
-
-        val children: Sequence<ExpandableView> = stack.childrenSequence
-        var remainingSpace: Float = availableSpace
-        var count = 0
-        var previous: ExpandableView? = null
-        val onLockscreen = true
-        val showableRows = children.filter { it.isShowable(onLockscreen) }
-        val showableRowsCount = showableRows.count()
-        log { "\tshowableRowsCount=$showableRowsCount "}
-
-        showableRows.forEachIndexed { i, current ->
-            val spaceNeeded = current.spaceNeeded(count, previous, stack, onLockscreen)
-            val spaceAfter = remainingSpace - spaceNeeded
-            previous = current
-            log { "\ti=$i spaceNeeded=$spaceNeeded remainingSpace=$remainingSpace " +
-                    "spaceAfter=$spaceAfter" }
-
-            if (remainingSpace - spaceNeeded >= 0 && count < maxKeyguardNotifications) {
-                count += 1
-                remainingSpace -= spaceNeeded
-            } else if (remainingSpace - spaceNeeded > -shelfHeight && i == showableRowsCount - 1) {
-                log { "Show all notifications. Shelf not needed." }
-                // If this is the last one, and it fits using the space shelf would use, then we can
-                // display it, as the shelf will not be needed (as all notifications are shown).
-                return count + 1
-            } else {
-                log {
-                    "No more fit. Returning $count. Space used: ${availableSpace - remainingSpace}"
-                }
-                return count
-            }
-        }
-        log { "All fit. Returning $count" }
-        return count
+        return maxNotifications
     }
 
     /**
@@ -119,47 +97,60 @@
      * @param stack stack containing notifications as children.
      * @param maxNotifications Maximum number of notifications. When reached, the others will go
      * into the shelf.
-     * @param shelfHeight height of the shelf. It might be zero.
+     * @param shelfIntrinsicHeight height of the shelf, without any padding. It might be zero.
      *
      * @return height of the stack, including shelf height, if needed.
      */
     fun computeHeight(
         stack: NotificationStackScrollLayout,
         maxNotifications: Int,
-        shelfHeight: Float
+        shelfIntrinsicHeight: Float
     ): Float {
-        val children: Sequence<ExpandableView> = stack.childrenSequence
-        val maxNotificationsArg = infiniteIfNegative(maxNotifications)
+        val heightPerMaxNotifications =
+            computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight)
+        val height =
+            heightPerMaxNotifications.elementAtOrElse(maxNotifications) {
+                heightPerMaxNotifications.last() // Height with all notifications visible.
+            }
+        log { "computeHeight(maxNotifications=$maxNotifications) -> $height" }
+        return height
+    }
+
+    /** The ith result in the sequence is the height with ith max notifications. */
+    private fun computeHeightPerNotificationLimit(
+        stack: NotificationStackScrollLayout,
+        shelfIntrinsicHeight: Float
+    ): Sequence<Float> = sequence {
+        val children = stack.showableChildren().toList()
         var height = 0f
         var previous: ExpandableView? = null
-        var count = 0
         val onLockscreen = onLockscreen()
 
-        log { "computeHeight(maxNotification=$maxNotifications, shelf=$shelfHeight" }
-        children.filter { it.isShowable(onLockscreen) }.forEach { current ->
-            if (count < maxNotificationsArg) {
-                val spaceNeeded = current.spaceNeeded(count, previous, stack, onLockscreen)
-                log { "\ti=$count spaceNeeded=$spaceNeeded" }
-                height += spaceNeeded
-                count += 1
-            } else {
-                height += current.calculateGapAndDividerHeight(stack, previous, count)
-                height += shelfHeight
-                log { "returning height with shelf -> $height" }
-                return height
-            }
-            previous = current
+        yield(dividerHeight + shelfIntrinsicHeight) // Only shelf.
+
+        children.forEachIndexed { i, currentNotification ->
+            height += currentNotification.spaceNeeded(i, previous, stack, onLockscreen)
+            previous = currentNotification
+
+            val shelfHeight =
+                if (i == children.lastIndex) {
+                    0f // No shelf needed.
+                } else {
+                    val spaceBeforeShelf =
+                        calculateGapAndDividerHeight(
+                            stack, previous = currentNotification, current = children[i + 1], i)
+                    spaceBeforeShelf + shelfIntrinsicHeight
+                }
+
+            yield(height + shelfHeight)
         }
-        log { "Returning height without shelf -> $height" }
-        return height
     }
 
     fun updateResources() {
         maxKeyguardNotifications =
             infiniteIfNegative(resources.getInteger(R.integer.keyguard_max_notification_count))
 
-        dividerHeight =
-            max(1, resources.getDimensionPixelSize(R.dimen.notification_divider_height))
+        dividerHeight = max(1, resources.getDimensionPixelSize(R.dimen.notification_divider_height))
     }
 
     private val NotificationStackScrollLayout.childrenSequence: Sequence<ExpandableView>
@@ -180,7 +171,7 @@
             } else {
                 intrinsicHeight.toFloat()
             }
-        size += calculateGapAndDividerHeight(stack, previousView, visibleIndex)
+        size += calculateGapAndDividerHeight(stack, previousView, current = this, visibleIndex)
         return size
     }
 
@@ -200,18 +191,22 @@
         return true
     }
 
-    private fun ExpandableView.calculateGapAndDividerHeight(
+    private fun calculateGapAndDividerHeight(
         stack: NotificationStackScrollLayout,
         previous: ExpandableView?,
+        current: ExpandableView?,
         visibleIndex: Int
-    ) : Float {
-        var height = stack.calculateGapHeight(previous, /* current= */ this, visibleIndex)
+    ): Float {
+        var height = stack.calculateGapHeight(previous, current, visibleIndex)
         if (visibleIndex != 0) {
             height += dividerHeight
         }
         return height
     }
 
+    private fun NotificationStackScrollLayout.showableChildren() =
+        this.childrenSequence.filter { it.isShowable(onLockscreen()) }
+
     /**
      * Can a view be shown on the lockscreen when calculating the number of allowed notifications to
      * show?
@@ -240,4 +235,8 @@
         } else {
             v
         }
+
+    /** Returns the last index where [predicate] returns true, or -1 if it was always false. */
+    private fun <T> Sequence<T>.lastIndexWhile(predicate: (T) -> Boolean): Int =
+        takeWhile(predicate).count() - 1
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index c77b0d6..5e81b5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -4404,8 +4404,7 @@
                 @Override
                 public void onDozeAmountChanged(float linear, float eased) {
                     if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)
-                            && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)
-                            && !mBiometricUnlockController.isWakeAndUnlock()) {
+                            && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
                         mLightRevealScrim.setRevealAmount(1f - linear);
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index a19d5f1..54d39fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -31,8 +31,6 @@
 import android.util.Log;
 import android.util.MathUtils;
 
-import android.util.Log;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
@@ -293,11 +291,6 @@
     }
 
     public void updateControlScreenOff() {
-        Log.i("TEST", "Display needs blanking?" + getDisplayNeedsBlanking());
-        Log.i("TEST", "Should control screen off?" + shouldControlUnlockedScreenOff());
-        Log.i("TEST", "alwaysOn?" + getAlwaysOn());
-        Log.i("TEST", "keyguard showing?" + mKeyguardShowing);
-        Log.i("TEST", "Flag enabled? " + mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS));
         if (!getDisplayNeedsBlanking()) {
             final boolean controlScreenOff =
                     getAlwaysOn() && (mKeyguardShowing || shouldControlUnlockedScreenOff());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index b6ad9f7..16fddb42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -93,7 +93,8 @@
         mDisplayId = mContext.getDisplayId();
     }
 
-    public void destroy(Context context) {
+    /** Call to cleanup the LightBarTransitionsController when done with it. */
+    public void destroy() {
         mCommandQueue.removeCallback(this);
         mStatusBarStateController.removeCallback(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index a6b5c4d..cb2d5b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -317,8 +317,6 @@
     private boolean mShouldUseSplitNotificationShade;
     // The bottom padding reserved for elements of the keyguard measuring notifications
     private float mKeyguardNotificationBottomPadding;
-    // Space available for notifications.
-    private float mKeyguardNotificationAvailableSpace;
     // Current max allowed keyguard notifications determined by measuring the panel
     private int mMaxAllowedKeyguardNotifications;
 
@@ -1245,8 +1243,6 @@
                     mMaxAllowedKeyguardNotifications);
             mNotificationStackScrollLayoutController.setKeyguardBottomPaddingForDebug(
                     mKeyguardNotificationBottomPadding);
-            mNotificationStackScrollLayoutController.mKeyguardNotificationAvailableSpaceForDebug(
-                    mKeyguardNotificationAvailableSpace);
         } else {
             // no max when not on the keyguard
             mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(-1);
@@ -1468,13 +1464,11 @@
      * @return the maximum keyguard notifications that can fit on the screen
      */
     private int computeMaxKeyguardNotifications() {
-        int notificationPadding = Math.max(
-                1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height));
         float topPadding = mNotificationStackScrollLayoutController.getTopPadding();
-        float shelfHeight =
+        float shelfIntrinsicHeight =
                 mNotificationShelfController.getVisibility() == View.GONE
                         ? 0
-                        : mNotificationShelfController.getIntrinsicHeight() + notificationPadding;
+                        : mNotificationShelfController.getIntrinsicHeight();
 
         // Padding to add to the bottom of the stack to keep a minimum distance from the top of
         // the lock icon.
@@ -1493,13 +1487,11 @@
         float availableSpace =
                 mNotificationStackScrollLayoutController.getHeight()
                         - topPadding
-                        - shelfHeight
                         - bottomPadding;
-        mKeyguardNotificationAvailableSpace = availableSpace;
 
         return mNotificationStackSizeCalculator.computeMaxKeyguardNotifications(
                 mNotificationStackScrollLayoutController.getView(), availableSpace,
-                shelfHeight);
+                shelfIntrinsicHeight);
     }
 
     private void updateClock() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 518a918..d492c57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -797,7 +797,7 @@
             mExpandedFraction = Math.min(1f,
                     maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight);
             mAmbientState.setExpansionFraction(mStatusBarKeyguardViewManager.bouncerIsInTransit()
-                    ? BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(mExpandedFraction)
+                    ? BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(mExpandedFraction)
                     : mExpandedFraction);
             onHeightUpdated(mExpandedHeight);
             updatePanelExpansionAndVisibility();
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 26ffdf2..290df3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -292,9 +292,7 @@
         mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled());
 
         // managed profile
-        mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status,
-                getManagedProfileAccessibilityString());
-        mIconController.setIconVisibility(mSlotManagedProfile, mManagedProfileIconVisible);
+        updateManagedProfile();
 
         // data saver
         mIconController.setIcon(mSlotDataSaver, R.drawable.stat_sys_data_saver,
@@ -521,7 +519,7 @@
     }
 
     private void updateManagedProfile() {
-        // getLastResumedActivityUserId needds to acquire the AM lock, which may be contended in
+        // getLastResumedActivityUserId needs to acquire the AM lock, which may be contended in
         // some cases. Since it doesn't really matter here whether it's updated in this frame
         // or in the next one, we call this method from our UI offload thread.
         mUiBgExecutor.execute(() -> {
@@ -529,6 +527,7 @@
             try {
                 userId = ActivityTaskManager.getService().getLastResumedActivityUserId();
                 boolean isManagedProfile = mUserManager.isManagedProfile(userId);
+                String accessibilityString = getManagedProfileAccessibilityString();
                 mHandler.post(() -> {
                     final boolean showIcon;
                     if (isManagedProfile && (!mKeyguardStateController.isShowing()
@@ -536,7 +535,7 @@
                         showIcon = true;
                         mIconController.setIcon(mSlotManagedProfile,
                                 R.drawable.stat_sys_managed_profile_status,
-                                getManagedProfileAccessibilityString());
+                                accessibilityString);
                     } else {
                         showIcon = false;
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index fd6503a..cc2ff3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -790,7 +790,7 @@
 
             if (mBouncerHiddenFraction != KeyguardBouncer.EXPANSION_HIDDEN) {
                 final float interpolatedFraction =
-                        BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(
+                        BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(
                                 mBouncerHiddenFraction);
                 mBehindAlpha = MathUtils.lerp(mDefaultScrimAlpha, mBehindAlpha,
                         interpolatedFraction);
@@ -1076,7 +1076,7 @@
     private float getInterpolatedFraction() {
         if (mStatusBarKeyguardViewManager.bouncerIsInTransit()) {
             return BouncerPanelExpansionCalculator
-                    .getBackScrimScaledExpansion(mPanelExpansionFraction);
+                    .aboutToShowBouncerProgress(mPanelExpansionFraction);
         }
         return ShadeInterpolation.getNotificationScrimAlpha(mPanelExpansionFraction);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index ea935be..935f87d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -17,6 +17,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.KeyguardViewMediator
 import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.statusbar.CircleReveal
 import com.android.systemui.statusbar.LightRevealScrim
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.StatusBarStateControllerImpl
@@ -85,7 +86,9 @@
         duration = LIGHT_REVEAL_ANIMATION_DURATION
         interpolator = Interpolators.LINEAR
         addUpdateListener {
-            lightRevealScrim.revealAmount = it.animatedValue as Float
+            if (lightRevealScrim.revealEffect !is CircleReveal) {
+                lightRevealScrim.revealAmount = it.animatedValue as Float
+            }
             if (lightRevealScrim.isScrimAlmostOccludes &&
                     interactionJankMonitor.isInstrumenting(CUJ_SCREEN_OFF)) {
                 // ends the instrument when the scrim almost occludes the screen.
@@ -95,9 +98,9 @@
         }
         addListener(object : AnimatorListenerAdapter() {
             override fun onAnimationCancel(animation: Animator?) {
-                lightRevealScrim.revealAmount = 1f
-                lightRevealAnimationPlaying = false
-                interactionJankMonitor.cancel(CUJ_SCREEN_OFF)
+                if (lightRevealScrim.revealEffect !is CircleReveal) {
+                    lightRevealScrim.revealAmount = 1f
+                }
             }
 
             override fun onAnimationEnd(animation: Animator?) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 846e07f..a3f01c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -64,8 +64,6 @@
 import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.GuestResumeSessionReceiver;
-import com.android.systemui.Prefs;
-import com.android.systemui.Prefs.Key;
 import com.android.systemui.R;
 import com.android.systemui.SystemUISecondaryUserService;
 import com.android.systemui.animation.DialogLaunchAnimator;
@@ -84,6 +82,7 @@
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.telephony.TelephonyListenerManager;
 import com.android.systemui.user.CreateUserActivity;
+import com.android.systemui.util.settings.GlobalSettings;
 import com.android.systemui.util.settings.SecureSettings;
 
 import java.io.PrintWriter;
@@ -144,6 +143,7 @@
     // When false, there won't be any visual affordance to add a new user from the keyguard even if
     // the user is unlocked
     private boolean mAddUsersFromLockScreen;
+    private boolean mUserSwitcherEnabled;
     @VisibleForTesting
     boolean mPauseRefreshUsers;
     private int mSecondaryUser = UserHandle.USER_NULL;
@@ -160,6 +160,7 @@
     private FalsingManager mFalsingManager;
     private View mView;
     private String mCreateSupervisedUserPackage;
+    private GlobalSettings mGlobalSettings;
 
     @Inject
     public UserSwitcherController(Context context,
@@ -177,6 +178,7 @@
             FalsingManager falsingManager,
             TelephonyListenerManager telephonyListenerManager,
             SecureSettings secureSettings,
+            GlobalSettings globalSettings,
             @Background Executor bgExecutor,
             @LongRunning Executor longRunningExecutor,
             @Main Executor uiExecutor,
@@ -194,6 +196,7 @@
         mFalsingManager = falsingManager;
         mInteractionJankMonitor = interactionJankMonitor;
         mLatencyTracker = latencyTracker;
+        mGlobalSettings = globalSettings;
         mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(
                 this, mUserTracker, mUiEventLogger, secureSettings);
         mBgExecutor = bgExecutor;
@@ -237,8 +240,10 @@
             @Override
             public void onChange(boolean selfChange) {
                 mSimpleUserSwitcher = shouldUseSimpleUserSwitcher();
-                mAddUsersFromLockScreen = Settings.Global.getInt(mContext.getContentResolver(),
-                        Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
+                mAddUsersFromLockScreen = mGlobalSettings.getIntForUser(
+                        Settings.Global.ADD_USERS_WHEN_LOCKED, 0, UserHandle.USER_SYSTEM) != 0;
+                mUserSwitcherEnabled = mGlobalSettings.getIntForUser(
+                        Settings.Global.USER_SWITCHER_ENABLED, 0, UserHandle.USER_SYSTEM) != 0;
                 refreshUsers(UserHandle.USER_NULL);
             };
         };
@@ -246,6 +251,9 @@
                 Settings.Global.getUriFor(SIMPLE_USER_SWITCHER_GLOBAL_SETTING), true,
                 mSettingsObserver);
         mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.USER_SWITCHER_ENABLED), true,
+                mSettingsObserver);
+        mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.ADD_USERS_WHEN_LOCKED), true,
                 mSettingsObserver);
         mContext.getContentResolver().registerContentObserver(
@@ -314,6 +322,10 @@
             for (UserInfo info : infos) {
                 boolean isCurrent = currentId == info.id;
                 boolean switchToEnabled = canSwitchUsers || isCurrent;
+                if (!mUserSwitcherEnabled && !info.isPrimary()) {
+                    continue;
+                }
+
                 if (info.isEnabled()) {
                     if (info.isGuest()) {
                         // Tapping guest icon triggers remove and a user switch therefore
@@ -340,9 +352,6 @@
                     }
                 }
             }
-            if (records.size() > 1 || guestRecord != null) {
-                Prefs.putBoolean(mContext, Key.SEEN_MULTI_USER, true);
-            }
 
             if (guestRecord == null) {
                 if (mGuestUserAutoCreated) {
@@ -411,12 +420,14 @@
     }
 
     boolean canCreateGuest(boolean hasExistingGuest) {
-        return (currentUserCanCreateUsers() || anyoneCanCreateUsers())
+        return mUserSwitcherEnabled
+                && (currentUserCanCreateUsers() || anyoneCanCreateUsers())
                 && !hasExistingGuest;
     }
 
     boolean canCreateUser() {
-        return (currentUserCanCreateUsers() || anyoneCanCreateUsers())
+        return mUserSwitcherEnabled
+                && (currentUserCanCreateUsers() || anyoneCanCreateUsers())
                 && mUserManager.canAddMoreUsers(UserManager.USER_TYPE_FULL_SECONDARY);
     }
 
@@ -1034,8 +1045,8 @@
     private boolean shouldUseSimpleUserSwitcher() {
         int defaultSimpleUserSwitcher = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_expandLockScreenUserSwitcher) ? 1 : 0;
-        return Settings.Global.getInt(mContext.getContentResolver(),
-                SIMPLE_USER_SWITCHER_GLOBAL_SETTING, defaultSimpleUserSwitcher) != 0;
+        return mGlobalSettings.getIntForUser(SIMPLE_USER_SWITCHER_GLOBAL_SETTING,
+                defaultSimpleUserSwitcher, UserHandle.USER_SYSTEM) != 0;
     }
 
     public void startActivity(Intent intent) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt
index 6266bf1..f8fdd8d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt
@@ -29,29 +29,29 @@
 class BouncerPanelExpansionCalculatorTest : SysuiTestCase() {
     @Test
     fun testGetHostViewScaledExpansion() {
-        assertThat(BouncerPanelExpansionCalculator.getHostViewScaledExpansion(1f))
+        assertThat(BouncerPanelExpansionCalculator.showBouncerProgress(1f))
                 .isEqualTo(1f)
-        assertThat(BouncerPanelExpansionCalculator.getHostViewScaledExpansion(0.9f))
+        assertThat(BouncerPanelExpansionCalculator.showBouncerProgress(0.9f))
                 .isEqualTo(1f)
-        assertThat(BouncerPanelExpansionCalculator.getHostViewScaledExpansion(0.59f))
+        assertThat(BouncerPanelExpansionCalculator.showBouncerProgress(0.59f))
                 .isEqualTo(0f)
-        assertThat(BouncerPanelExpansionCalculator.getHostViewScaledExpansion(0f))
+        assertThat(BouncerPanelExpansionCalculator.showBouncerProgress(0f))
                 .isEqualTo(0f)
         assertEquals(BouncerPanelExpansionCalculator
-                .getHostViewScaledExpansion(0.8f), 2f / 3f, 0.01f)
+                .showBouncerProgress(0.8f), 2f / 3f, 0.01f)
     }
 
     @Test
     fun testGetBackScrimScaledExpansion() {
-        assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(1f))
+        assertThat(BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(1f))
                 .isEqualTo(1f)
         assertEquals(BouncerPanelExpansionCalculator
-                .getBackScrimScaledExpansion(0.95f), 1f / 2f, 0.01f)
-        assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0.9f))
+                .aboutToShowBouncerProgress(0.95f), 1f / 2f, 0.01f)
+        assertThat(BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(0.9f))
                 .isEqualTo(0f)
-        assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0.5f))
+        assertThat(BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(0.5f))
                 .isEqualTo(0f)
-        assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0f))
+        assertThat(BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(0f))
                 .isEqualTo(0f)
     }
 
@@ -63,9 +63,9 @@
                 .getKeyguardClockScaledExpansion(0.8f), 1f / 3f, 0.01f)
         assertThat(BouncerPanelExpansionCalculator.getKeyguardClockScaledExpansion(0.7f))
                 .isEqualTo(0f)
-        assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0.5f))
+        assertThat(BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(0.5f))
                 .isEqualTo(0f)
-        assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0f))
+        assertThat(BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(0f))
                 .isEqualTo(0f)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 50bd9b0..6bb994f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -27,14 +27,15 @@
 
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isA;
-import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -62,9 +63,11 @@
 import android.view.Display;
 import android.view.DisplayCutout;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.WindowMetrics;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 
@@ -103,7 +106,7 @@
     private SecureSettings mSecureSettings;
     private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
     private FakeThreadFactory mThreadFactory;
-    private ArrayList<DecorProvider> mDecorProviders;
+    private ArrayList<DecorProvider> mPrivacyDecorProviders;
     @Mock
     private Display mDisplay;
     @Mock
@@ -216,16 +219,43 @@
         }
     }
 
-    private void verifyRoundedCornerViewsVisibility(
+    @NonNull
+    private int[] getRoundCornerIdsFromOverlayId(@DisplayCutout.BoundsPosition int overlayId) {
+        switch (overlayId) {
+            case BOUNDS_POSITION_LEFT:
+                return new int[] {
+                        R.id.rounded_corner_top_left,
+                        R.id.rounded_corner_top_left };
+            case BOUNDS_POSITION_TOP:
+                return new int[] {
+                        R.id.rounded_corner_top_left,
+                        R.id.rounded_corner_top_right };
+            case BOUNDS_POSITION_RIGHT:
+                return new int[] {
+                        R.id.rounded_corner_top_right,
+                        R.id.rounded_corner_bottom_right };
+            case BOUNDS_POSITION_BOTTOM:
+                return new int[] {
+                        R.id.rounded_corner_bottom_left,
+                        R.id.rounded_corner_bottom_right };
+            default:
+                throw new IllegalArgumentException("unknown overlayId: " + overlayId);
+        }
+    }
+
+    private void verifyRoundedCornerViewsExist(
             @DisplayCutout.BoundsPosition final int overlayId,
-            @View.Visibility final int visibility) {
+            @View.Visibility final boolean isExist) {
         final View overlay = mScreenDecorations.mOverlays[overlayId].getRootView();
-        final View left = overlay.findViewById(R.id.left);
-        final View right = overlay.findViewById(R.id.right);
-        assertNotNull(left);
-        assertNotNull(right);
-        assertThat(left.getVisibility()).isEqualTo(visibility);
-        assertThat(right.getVisibility()).isEqualTo(visibility);
+        for (int id: getRoundCornerIdsFromOverlayId(overlayId)) {
+            final View view = overlay.findViewById(id);
+            if (isExist) {
+                assertNotNull(view);
+                assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
+            } else {
+                assertNull(view);
+            }
+        }
     }
 
     @Nullable
@@ -388,8 +418,8 @@
                 mScreenDecorations.mPrivacyDotShowingListener);
 
         // Rounded corner views shall not exist
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE);
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false);
 
         // Privacy dots shall exist but invisible
         verifyDotViewsVisibility(View.INVISIBLE);
@@ -417,8 +447,8 @@
         verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE);
 
         // Rounded corner views shall exist
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true);
 
         // Privacy dots shall not exist
         verifyDotViewsNullable(true);
@@ -447,8 +477,8 @@
         verify(mDotViewController, times(1)).setShowingListener(null);
 
         // Rounded corner views shall exist
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true);
 
         // Privacy dots shall exist but invisible
         verifyDotViewsVisibility(View.INVISIBLE);
@@ -488,21 +518,26 @@
 
         mScreenDecorations.start();
         View leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView()
-                .findViewById(R.id.left);
+                .findViewById(R.id.rounded_corner_top_left);
         View rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView()
-                .findViewById(R.id.right);
-        verify(mScreenDecorations, atLeastOnce())
-                .setSize(leftRoundedCorner, new Size(testTopRadius, testTopRadius));
-        verify(mScreenDecorations, atLeastOnce())
-                .setSize(rightRoundedCorner, new Size(testTopRadius, testTopRadius));
+                .findViewById(R.id.rounded_corner_top_right);
+        ViewGroup.LayoutParams leftParams = leftRoundedCorner.getLayoutParams();
+        ViewGroup.LayoutParams rightParams = rightRoundedCorner.getLayoutParams();
+        assertEquals(leftParams.width, testTopRadius);
+        assertEquals(leftParams.height, testTopRadius);
+        assertEquals(rightParams.width, testTopRadius);
+        assertEquals(rightParams.height, testTopRadius);
+
         leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView()
-                .findViewById(R.id.left);
+                .findViewById(R.id.rounded_corner_bottom_left);
         rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView()
-                .findViewById(R.id.right);
-        verify(mScreenDecorations, atLeastOnce())
-                .setSize(leftRoundedCorner, new Size(testBottomRadius, testBottomRadius));
-        verify(mScreenDecorations, atLeastOnce())
-                .setSize(rightRoundedCorner, new Size(testBottomRadius, testBottomRadius));
+                .findViewById(R.id.rounded_corner_bottom_right);
+        leftParams = leftRoundedCorner.getLayoutParams();
+        rightParams = rightRoundedCorner.getLayoutParams();
+        assertEquals(leftParams.width, testBottomRadius);
+        assertEquals(leftParams.height, testBottomRadius);
+        assertEquals(rightParams.width, testBottomRadius);
+        assertEquals(rightParams.height, testBottomRadius);
     }
 
     @Test
@@ -518,31 +553,27 @@
                 .when(mScreenDecorations).getCutout();
 
         mScreenDecorations.start();
-        final Size topRadius = new Size(testTopRadius, testTopRadius);
-        final Size bottomRadius = new Size(testBottomRadius, testBottomRadius);
-        View leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView()
-                .findViewById(R.id.left);
-        boolean isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_LEFT, R.id.left);
-        verify(mScreenDecorations, atLeastOnce())
-                .setSize(leftRoundedCorner, isTop ? topRadius : bottomRadius);
+        View topRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView()
+                .findViewById(R.id.rounded_corner_top_left);
+        View bottomRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView()
+                .findViewById(R.id.rounded_corner_bottom_left);
+        ViewGroup.LayoutParams topParams = topRoundedCorner.getLayoutParams();
+        ViewGroup.LayoutParams bottomParams = bottomRoundedCorner.getLayoutParams();
+        assertEquals(topParams.width, testTopRadius);
+        assertEquals(topParams.height, testTopRadius);
+        assertEquals(bottomParams.width, testBottomRadius);
+        assertEquals(bottomParams.height, testBottomRadius);
 
-        View rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView()
-                .findViewById(R.id.right);
-        isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_LEFT, R.id.right);
-        verify(mScreenDecorations, atLeastOnce())
-                .setSize(rightRoundedCorner, isTop ? topRadius : bottomRadius);
-
-        leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView()
-                .findViewById(R.id.left);
-        isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_RIGHT, R.id.left);
-        verify(mScreenDecorations, atLeastOnce())
-                .setSize(leftRoundedCorner, isTop ? topRadius : bottomRadius);
-
-        rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView()
-                .findViewById(R.id.right);
-        isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_RIGHT, R.id.right);
-        verify(mScreenDecorations, atLeastOnce())
-                .setSize(rightRoundedCorner, isTop ? topRadius : bottomRadius);
+        topRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView()
+                .findViewById(R.id.rounded_corner_top_right);
+        bottomRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView()
+                .findViewById(R.id.rounded_corner_bottom_right);
+        topParams = topRoundedCorner.getLayoutParams();
+        bottomParams = bottomRoundedCorner.getLayoutParams();
+        assertEquals(topParams.width, testTopRadius);
+        assertEquals(topParams.height, testTopRadius);
+        assertEquals(bottomParams.width, testBottomRadius);
+        assertEquals(bottomParams.height, testBottomRadius);
     }
 
     @Test
@@ -562,8 +593,8 @@
         verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE);
 
         // Rounded corner views shall exist
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true);
 
         // Privacy dots shall not exist
         verifyDotViewsNullable(true);
@@ -598,8 +629,8 @@
         verify(mDotViewController, times(1)).setShowingListener(null);
 
         // Rounded corner views shall exist
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true);
 
         // Privacy dots shall exist but invisible
         verifyDotViewsVisibility(View.INVISIBLE);
@@ -660,10 +691,10 @@
 
         // Top rounded corner views shall exist because of cutout
         // but be gone because of no rounded corner
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false);
         // Bottom rounded corner views shall exist because of privacy dot
         // but be gone because of no rounded corner
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false);
 
         // Privacy dots shall exist but invisible
         verifyDotViewsVisibility(View.INVISIBLE);
@@ -691,7 +722,7 @@
 
         // Left rounded corner views shall exist because of cutout
         // but be gone because of no rounded corner
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_LEFT, View.GONE);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_LEFT, false);
 
         // Top privacy dots shall not exist because of no privacy
         verifyDotViewsNullable(true);
@@ -745,8 +776,8 @@
         verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE);
 
         // Rounded corner views shall exist
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true);
 
         // Top privacy dots shall not exist because of no privacy dot
         verifyDotViewsNullable(true);
@@ -775,8 +806,8 @@
         verify(mDotViewController, times(1)).setShowingListener(null);
 
         // Rounded corner views shall exist
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true);
 
         // Top privacy dots shall exist but invisible
         verifyDotViewsVisibility(View.INVISIBLE);
@@ -914,7 +945,7 @@
         verify(mDotViewController, times(2)).setShowingListener(null);
 
         // Verify each privacy dot id appears only once
-        mDecorProviders.stream().map(DecorProvider::getViewId).forEach(viewId -> {
+        mPrivacyDecorProviders.stream().map(DecorProvider::getViewId).forEach(viewId -> {
             int findCount = 0;
             for (OverlayWindow overlay: mScreenDecorations.mOverlays) {
                 if (overlay == null) {
@@ -968,8 +999,8 @@
         // Both top and bottom windows should be added with INVISIBLE because of only privacy dot,
         // but rounded corners visibility shall be gone because of no rounding.
         verifyOverlaysExistAndAdded(false, true, false, true, View.INVISIBLE);
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE);
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false);
         verify(mDotViewController, times(1)).initialize(any(), any(), any(), any());
         verify(mDotViewController, times(1)).setShowingListener(
                 mScreenDecorations.mPrivacyDotShowingListener);
@@ -982,8 +1013,8 @@
         // Both top and bottom windows should be added with VISIBLE because of privacy dot and
         // cutout, but rounded corners visibility shall be gone because of no rounding.
         verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE);
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE);
-        verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false);
+        verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false);
         verify(mDotViewController, times(2)).initialize(any(), any(), any(), any());
         verify(mDotViewController, times(1)).setShowingListener(null);
     }
@@ -1297,6 +1328,48 @@
         verify(cutoutView, times(1)).onDisplayChanged(1);
     }
 
+    @Test
+    public void testHasSameProvidersWithNullOverlays() {
+        setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
+                0 /* roundedPadding */, false /* multipleRadius */,
+                false /* fillCutout */, false /* privacyDot */);
+
+        mScreenDecorations.start();
+
+        final ArrayList<DecorProvider> newProviders = new ArrayList<>();
+        assertTrue(mScreenDecorations.hasSameProviders(newProviders));
+
+        newProviders.add(mPrivacyDotTopLeftDecorProvider);
+        assertFalse(mScreenDecorations.hasSameProviders(newProviders));
+
+        newProviders.add(mPrivacyDotTopRightDecorProvider);
+        assertFalse(mScreenDecorations.hasSameProviders(newProviders));
+    }
+
+    @Test
+    public void testHasSameProvidersWithPrivacyDots() {
+        setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
+                0 /* roundedPadding */, false /* multipleRadius */,
+                true /* fillCutout */, true /* privacyDot */);
+
+        mScreenDecorations.start();
+
+        final ArrayList<DecorProvider> newProviders = new ArrayList<>();
+        assertFalse(mScreenDecorations.hasSameProviders(newProviders));
+
+        newProviders.add(mPrivacyDotTopLeftDecorProvider);
+        assertFalse(mScreenDecorations.hasSameProviders(newProviders));
+
+        newProviders.add(mPrivacyDotTopRightDecorProvider);
+        assertFalse(mScreenDecorations.hasSameProviders(newProviders));
+
+        newProviders.add(mPrivacyDotBottomLeftDecorProvider);
+        assertFalse(mScreenDecorations.hasSameProviders(newProviders));
+
+        newProviders.add(mPrivacyDotBottomRightDecorProvider);
+        assertTrue(mScreenDecorations.hasSameProviders(newProviders));
+    }
+
     private void setupResources(int radius, int radiusTop, int radiusBottom, int roundedPadding,
             boolean multipleRadius, boolean fillCutout, boolean privacyDot) {
         mContext.getOrCreateTestableResources().addOverride(
@@ -1336,14 +1409,14 @@
         mContext.getOrCreateTestableResources().addOverride(
                 com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, fillCutout);
 
-        mDecorProviders = new ArrayList<>();
+        mPrivacyDecorProviders = new ArrayList<>();
         if (privacyDot) {
-            mDecorProviders.add(mPrivacyDotTopLeftDecorProvider);
-            mDecorProviders.add(mPrivacyDotTopRightDecorProvider);
-            mDecorProviders.add(mPrivacyDotBottomLeftDecorProvider);
-            mDecorProviders.add(mPrivacyDotBottomRightDecorProvider);
+            mPrivacyDecorProviders.add(mPrivacyDotTopLeftDecorProvider);
+            mPrivacyDecorProviders.add(mPrivacyDotTopRightDecorProvider);
+            mPrivacyDecorProviders.add(mPrivacyDotBottomLeftDecorProvider);
+            mPrivacyDecorProviders.add(mPrivacyDotBottomRightDecorProvider);
         }
-        when(mPrivacyDotDecorProviderFactory.getProviders()).thenReturn(mDecorProviders);
+        when(mPrivacyDotDecorProviderFactory.getProviders()).thenReturn(mPrivacyDecorProviders);
         when(mPrivacyDotDecorProviderFactory.getHasProviders()).thenReturn(privacyDot);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 666c9e4..2341928 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -40,6 +40,9 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Rule
@@ -314,7 +317,8 @@
             wakefulnessLifecycle,
             userManager,
             lockPatternUtils,
-            Handler(TestableLooper.get(this).looper)
+            Handler(TestableLooper.get(this).looper),
+            FakeExecutor(FakeSystemClock())
         )
 
         if (addToView) {
@@ -331,10 +335,11 @@
         wakefulnessLifecycle: WakefulnessLifecycle,
         userManager: UserManager,
         lockPatternUtils: LockPatternUtils,
-        mainHandler: Handler
+        mainHandler: Handler,
+        bgExecutor: DelayableExecutor
     ) : AuthContainerView(
         config, fpProps, faceProps,
-        wakefulnessLifecycle, userManager, lockPatternUtils, mainHandler
+        wakefulnessLifecycle, userManager, lockPatternUtils, mainHandler, bgExecutor
     ) {
         override fun postOnAnimation(runnable: Runnable) {
             runnable.run()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 190228d..4858ab5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -80,8 +80,11 @@
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.concurrency.Execution;
 import com.android.systemui.util.concurrency.FakeExecution;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -156,6 +159,7 @@
     private Execution mExecution;
     private TestableLooper mTestableLooper;
     private Handler mHandler;
+    private DelayableExecutor mBackgroundExecutor;
     private TestableAuthController mAuthController;
 
     @Before
@@ -164,6 +168,7 @@
         mExecution = new FakeExecution();
         mTestableLooper = TestableLooper.get(this);
         mHandler = new Handler(mTestableLooper.getLooper());
+        mBackgroundExecutor = new FakeExecutor(new FakeSystemClock());
 
         when(mContextSpy.getPackageManager()).thenReturn(mPackageManager);
         when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE))
@@ -759,11 +764,12 @@
             super(context, execution, commandQueue, activityTaskManager, windowManager,
                     fingerprintManager, faceManager, udfpsControllerFactory,
                     sidefpsControllerFactory, mDisplayManager, mWakefulnessLifecycle,
-                    mUserManager, mLockPatternUtils, statusBarStateController, mHandler);
+                    mUserManager, mLockPatternUtils, statusBarStateController, mHandler,
+                    mBackgroundExecutor);
         }
 
         @Override
-        protected AuthDialog buildDialog(PromptInfo promptInfo,
+        protected AuthDialog buildDialog(DelayableExecutor bgExecutor, PromptInfo promptInfo,
                 boolean requireConfirmation, int userId, int[] sensorIds,
                 String opPackageName, boolean skipIntro, long operationId, long requestId,
                 @BiometricManager.BiometricMultiSensorMode int multiSensorConfig,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt
index e95eb4e..f5990be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt
@@ -42,6 +42,7 @@
 import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito
+import org.mockito.Mockito.`when`
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
@@ -81,6 +82,8 @@
     @Mock
     private lateinit var unregisterFunction: BroadcastReceiver.() -> Unit
     @Mock
+    private lateinit var isPendingRemovalFunction: (BroadcastReceiver, Int) -> Boolean
+    @Mock
     private lateinit var receiver1: BroadcastReceiver
     @Mock
     private lateinit var receiver2: BroadcastReceiver
@@ -98,13 +101,16 @@
         MockitoAnnotations.initMocks(this)
         executor = FakeExecutor(FakeSystemClock())
 
+        `when`(isPendingRemovalFunction(any(), anyInt())).thenReturn(false)
+
         actionReceiver = ActionReceiver(
                 ACTION1,
                 USER.identifier,
                 registerFunction,
                 unregisterFunction,
                 executor,
-                logger
+                logger,
+                isPendingRemovalFunction
         )
     }
 
@@ -249,6 +255,20 @@
         verify(logger).logBroadcastDispatched(anyInt(), eq(ACTION1), sameNotNull(receiver1))
     }
 
+    @Test
+    fun testBroadcastNotDispatchingOnPendingRemoval() {
+        `when`(isPendingRemovalFunction(receiver1, USER.identifier)).thenReturn(true)
+
+        val receiverData = ReceiverData(receiver1, IntentFilter(ACTION1), directExecutor, USER)
+
+        actionReceiver.addReceiverData(receiverData)
+
+        val intent = Intent(ACTION1)
+        actionReceiver.onReceive(mContext, intent)
+        executor.runAllReady()
+        verify(receiver1, never()).onReceive(any(), eq(intent))
+    }
+
     @Test(expected = IllegalStateException::class)
     fun testBroadcastWithWrongAction_throwsException() {
         actionReceiver.onReceive(mContext, Intent(ACTION2))
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 a1d1933..7795d2c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
@@ -41,6 +41,8 @@
 import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito.`when`
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.inOrder
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
@@ -85,6 +87,8 @@
     private lateinit var logger: BroadcastDispatcherLogger
     @Mock
     private lateinit var userTracker: UserTracker
+    @Mock
+    private lateinit var removalPendingStore: PendingRemovalStore
 
     private lateinit var executor: Executor
 
@@ -108,6 +112,7 @@
                 mock(DumpManager::class.java),
                 logger,
                 userTracker,
+                removalPendingStore,
                 mapOf(0 to mockUBRUser0, 1 to mockUBRUser1))
 
         // These should be valid filters
@@ -325,6 +330,57 @@
         broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
     }
 
+    @Test
+    fun testTaggedReceiverForRemovalImmediately_allUsers() {
+        broadcastDispatcher.unregisterReceiver(broadcastReceiver)
+
+        verify(removalPendingStore).tagForRemoval(broadcastReceiver, UserHandle.USER_ALL)
+        verify(removalPendingStore, never()).clearPendingRemoval(eq(broadcastReceiver), anyInt())
+    }
+
+    @Test
+    fun testTaggedReceiverForRemovalImmediately_singleUser() {
+        val user = 0
+        broadcastDispatcher.unregisterReceiverForUser(broadcastReceiver, UserHandle.of(user))
+
+        verify(removalPendingStore).tagForRemoval(broadcastReceiver, user)
+        verify(removalPendingStore, never()).clearPendingRemoval(eq(broadcastReceiver), anyInt())
+    }
+
+    @Test
+    fun testUnregisterReceiverClearsPendingRemovalAfterRemoving_allUsers() {
+        broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, null, user0)
+        broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, null, user1)
+
+        broadcastDispatcher.unregisterReceiver(broadcastReceiver)
+
+        testableLooper.processAllMessages()
+
+        val inOrderUser0 = inOrder(mockUBRUser0, removalPendingStore)
+        inOrderUser0.verify(mockUBRUser0).unregisterReceiver(broadcastReceiver)
+        inOrderUser0.verify(removalPendingStore)
+            .clearPendingRemoval(broadcastReceiver, UserHandle.USER_ALL)
+
+        val inOrderUser1 = inOrder(mockUBRUser1, removalPendingStore)
+        inOrderUser1.verify(mockUBRUser1).unregisterReceiver(broadcastReceiver)
+        inOrderUser1.verify(removalPendingStore)
+            .clearPendingRemoval(broadcastReceiver, UserHandle.USER_ALL)
+    }
+
+    @Test
+    fun testUnregisterReceiverclearPendingRemovalAfterRemoving_singleUser() {
+        broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, null, user1)
+
+        broadcastDispatcher.unregisterReceiverForUser(broadcastReceiver, user1)
+
+        testableLooper.processAllMessages()
+
+        val inOrderUser1 = inOrder(mockUBRUser1, removalPendingStore)
+        inOrderUser1.verify(mockUBRUser1).unregisterReceiver(broadcastReceiver)
+        inOrderUser1.verify(removalPendingStore)
+            .clearPendingRemoval(broadcastReceiver, user1.identifier)
+    }
+
     private fun setUserMock(mockContext: Context, user: UserHandle) {
         `when`(mockContext.user).thenReturn(user)
         `when`(mockContext.userId).thenReturn(user.identifier)
@@ -337,8 +393,17 @@
         dumpManager: DumpManager,
         logger: BroadcastDispatcherLogger,
         userTracker: UserTracker,
+        removalPendingStore: PendingRemovalStore,
         var mockUBRMap: Map<Int, UserBroadcastDispatcher>
-    ) : BroadcastDispatcher(context, bgLooper, executor, dumpManager, logger, userTracker) {
+    ) : BroadcastDispatcher(
+        context,
+        bgLooper,
+        executor,
+        dumpManager,
+        logger,
+        userTracker,
+        removalPendingStore
+    ) {
         override fun createUBRForUser(userId: Int): UserBroadcastDispatcher {
             return mockUBRMap.getOrDefault(userId, mock(UserBroadcastDispatcher::class.java))
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt
new file mode 100644
index 0000000..43d2cb8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt
@@ -0,0 +1,81 @@
+package com.android.systemui.broadcast
+
+import android.content.BroadcastReceiver
+import android.os.UserHandle
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class PendingRemovalStoreTest : SysuiTestCase() {
+
+    @Mock
+    private lateinit var logger: BroadcastDispatcherLogger
+    @Mock
+    private lateinit var receiverOne: BroadcastReceiver
+    @Mock
+    private lateinit var receiverTwo: BroadcastReceiver
+
+    private lateinit var store: PendingRemovalStore
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        store = PendingRemovalStore(logger)
+    }
+
+    @Test
+    fun testTagForRemoval_logged() {
+        val user = 10
+        store.tagForRemoval(receiverOne, 10)
+
+        verify(logger).logTagForRemoval(user, receiverOne)
+    }
+
+    @Test
+    fun testClearedPendingRemoval_logged() {
+        val user = UserHandle.USER_ALL
+        store.clearPendingRemoval(receiverOne, user)
+
+        verify(logger).logClearedAfterRemoval(user, receiverOne)
+    }
+
+    @Test
+    fun testTaggedReceiverMarkedAsPending_specificUser() {
+        val user = 10
+        store.tagForRemoval(receiverOne, user)
+
+        assertThat(store.isPendingRemoval(receiverOne, user)).isTrue()
+        assertThat(store.isPendingRemoval(receiverOne, user + 1)).isFalse()
+        assertThat(store.isPendingRemoval(receiverOne, UserHandle.USER_ALL)).isFalse()
+    }
+
+    @Test
+    fun testTaggedReceiverMarkedAsPending_allUsers() {
+        val user = 10
+        store.tagForRemoval(receiverOne, UserHandle.USER_ALL)
+
+        assertThat(store.isPendingRemoval(receiverOne, user)).isTrue()
+        assertThat(store.isPendingRemoval(receiverOne, user + 1)).isTrue()
+        assertThat(store.isPendingRemoval(receiverOne, UserHandle.USER_ALL)).isTrue()
+    }
+
+    @Test
+    fun testOnlyBlockCorrectReceiver() {
+        val user = 10
+        store.tagForRemoval(receiverOne, user)
+
+        assertThat(store.isPendingRemoval(receiverOne, user)).isTrue()
+        assertThat(store.isPendingRemoval(receiverTwo, user)).isFalse()
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
index 116b81d..39e4467 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
@@ -68,6 +68,8 @@
     private lateinit var mockContext: Context
     @Mock
     private lateinit var logger: BroadcastDispatcherLogger
+    @Mock
+    private lateinit var removalPendingStore: PendingRemovalStore
 
     private lateinit var testableLooper: TestableLooper
     private lateinit var userBroadcastDispatcher: UserBroadcastDispatcher
@@ -84,7 +86,13 @@
         fakeExecutor = FakeExecutor(FakeSystemClock())
 
         userBroadcastDispatcher = object : UserBroadcastDispatcher(
-                mockContext, USER_ID, testableLooper.looper, mock(Executor::class.java), logger) {
+                mockContext,
+                USER_ID,
+                testableLooper.looper,
+                mock(Executor::class.java),
+                logger,
+                removalPendingStore
+        ) {
             override fun createActionReceiver(
                 action: String,
                 permission: String?,
@@ -216,7 +224,8 @@
                 USER_ID,
                 testableLooper.looper,
                 fakeExecutor,
-                logger
+                logger,
+                removalPendingStore
         )
         uBR.registerReceiver(
                 ReceiverData(
@@ -243,7 +252,8 @@
             USER_ID,
             testableLooper.looper,
             fakeExecutor,
-            logger
+            logger,
+            removalPendingStore
         )
         uBR.registerReceiver(
             ReceiverData(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
index ca74df0..69366fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
@@ -19,25 +19,19 @@
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import android.view.DisplayCutout
-import android.view.LayoutInflater
 import android.view.Surface
 import android.view.View
-import android.view.ViewGroup
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.mockito.eq
 import org.junit.Assert
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.never
 import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-import org.mockito.Mockito.`when` as whenever
 
 @RunWith(AndroidTestingRunner::class)
 @RunWithLooper(setAsMainLooper = true)
@@ -45,62 +39,144 @@
 class OverlayWindowTest : SysuiTestCase() {
 
     companion object {
-        private val TEST_DECOR_VIEW_ID = R.id.privacy_dot_bottom_right_container
-        private val TEST_DECOR_LAYOUT_ID = R.layout.privacy_dot_bottom_right
+        private val TEST_DECOR_VIEW_ID_1 = R.id.privacy_dot_top_left_container
+        private val TEST_DECOR_VIEW_ID_2 = R.id.privacy_dot_bottom_left_container
+        private val TEST_DECOR_VIEW_ID_3 = R.id.privacy_dot_bottom_right_container
     }
 
     private lateinit var overlay: OverlayWindow
-
-    @Mock private lateinit var layoutInflater: LayoutInflater
-    @Mock private lateinit var decorProvider: DecorProvider
+    private lateinit var decorProvider1: DecorProvider
+    private lateinit var decorProvider2: DecorProvider
+    private lateinit var decorProvider3: DecorProvider
 
     @Before
     fun setUp() {
-        MockitoAnnotations.initMocks(this)
+        decorProvider1 = spy(PrivacyDotCornerDecorProviderImpl(
+                viewId = TEST_DECOR_VIEW_ID_1,
+                alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP,
+                alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT,
+                layoutId = R.layout.privacy_dot_top_left))
+        decorProvider2 = spy(PrivacyDotCornerDecorProviderImpl(
+                viewId = TEST_DECOR_VIEW_ID_2,
+                alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM,
+                alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT,
+                layoutId = R.layout.privacy_dot_bottom_left))
+        decorProvider3 = spy(PrivacyDotCornerDecorProviderImpl(
+                viewId = TEST_DECOR_VIEW_ID_3,
+                alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM,
+                alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT,
+                layoutId = R.layout.privacy_dot_bottom_right))
 
-        layoutInflater = spy(LayoutInflater.from(mContext))
-
-        overlay = OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_RIGHT)
-
-        whenever(decorProvider.viewId).thenReturn(TEST_DECOR_VIEW_ID)
-        whenever(decorProvider.inflateView(
-            eq(layoutInflater),
-            eq(overlay.rootView),
-            anyInt())
-        ).then {
-            val layoutInflater = it.getArgument<LayoutInflater>(0)
-            val parent = it.getArgument<ViewGroup>(1)
-            layoutInflater.inflate(TEST_DECOR_LAYOUT_ID, parent)
-            return@then parent.getChildAt(parent.childCount - 1)
-        }
-    }
-
-    @Test
-    fun testAnyBoundsPositionShallNoExceptionForConstructor() {
-        OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_LEFT)
-        OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_TOP)
-        OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_RIGHT)
-        OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_BOTTOM)
+        overlay = OverlayWindow(mContext)
     }
 
     @Test
     fun testAddProvider() {
         @Surface.Rotation val rotation = Surface.ROTATION_270
-        overlay.addDecorProvider(decorProvider, rotation)
-        verify(decorProvider, Mockito.times(1)).inflateView(
-                eq(layoutInflater), eq(overlay.rootView), eq(rotation))
-        val viewFoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID)
-        Assert.assertNotNull(viewFoundFromRootView)
-        Assert.assertEquals(viewFoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID))
+        overlay.addDecorProvider(decorProvider1, rotation)
+        overlay.addDecorProvider(decorProvider2, rotation)
+
+        verify(decorProvider1, times(1)).inflateView(
+                mContext, overlay.rootView, rotation)
+        verify(decorProvider2, times(1)).inflateView(
+                mContext, overlay.rootView, rotation)
+
+        val view1FoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID_1)
+        Assert.assertNotNull(view1FoundFromRootView)
+        Assert.assertEquals(view1FoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID_1))
+        val view2FoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID_2)
+        Assert.assertNotNull(view2FoundFromRootView)
+        Assert.assertEquals(view2FoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID_2))
     }
 
     @Test
     fun testRemoveView() {
-        @Surface.Rotation val rotation = Surface.ROTATION_270
-        overlay.addDecorProvider(decorProvider, rotation)
-        overlay.removeView(TEST_DECOR_VIEW_ID)
-        val viewFoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID)
+        overlay.addDecorProvider(decorProvider1, Surface.ROTATION_270)
+        overlay.addDecorProvider(decorProvider2, Surface.ROTATION_270)
+        overlay.removeView(TEST_DECOR_VIEW_ID_1)
+
+        val viewFoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID_1)
         Assert.assertNull(viewFoundFromRootView)
-        Assert.assertNull(overlay.getView(TEST_DECOR_LAYOUT_ID))
+        Assert.assertNull(overlay.getView(TEST_DECOR_VIEW_ID_1))
+    }
+
+    @Test
+    fun testOnReloadResAndMeasureWithoutIds() {
+        overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0)
+        overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0)
+
+        overlay.onReloadResAndMeasure(
+                reloadToken = 1,
+                rotation = Surface.ROTATION_90,
+                displayUniqueId = null)
+        verify(decorProvider1, times(1)).onReloadResAndMeasure(
+                overlay.getView(TEST_DECOR_VIEW_ID_1)!!, 1, Surface.ROTATION_90, null)
+        verify(decorProvider2, times(1)).onReloadResAndMeasure(
+                overlay.getView(TEST_DECOR_VIEW_ID_2)!!, 1, Surface.ROTATION_90, null)
+    }
+
+    @Test
+    fun testOnReloadResAndMeasureWithIds() {
+        overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0)
+        overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0)
+
+        overlay.onReloadResAndMeasure(
+                filterIds = arrayOf(TEST_DECOR_VIEW_ID_2),
+                reloadToken = 1,
+                rotation = Surface.ROTATION_90,
+                displayUniqueId = null)
+        verify(decorProvider1, never()).onReloadResAndMeasure(
+                overlay.getView(TEST_DECOR_VIEW_ID_1)!!, 1, Surface.ROTATION_90, null)
+        verify(decorProvider2, times(1)).onReloadResAndMeasure(
+                overlay.getView(TEST_DECOR_VIEW_ID_2)!!, 1, Surface.ROTATION_90, null)
+    }
+
+    @Test
+    fun testRemoveRedundantViewsWithNullParameter() {
+        overlay.addDecorProvider(decorProvider1, Surface.ROTATION_270)
+        overlay.addDecorProvider(decorProvider2, Surface.ROTATION_270)
+
+        overlay.removeRedundantViews(null)
+
+        Assert.assertNull(overlay.getView(TEST_DECOR_VIEW_ID_1))
+        Assert.assertNull(overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_1))
+        Assert.assertNull(overlay.getView(TEST_DECOR_VIEW_ID_2))
+        Assert.assertNull(overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_2))
+    }
+
+    @Test
+    fun testRemoveRedundantViewsWith2Providers() {
+        overlay.addDecorProvider(decorProvider1, Surface.ROTATION_270)
+        overlay.addDecorProvider(decorProvider2, Surface.ROTATION_270)
+
+        overlay.removeRedundantViews(IntArray(2).apply {
+            this[0] = TEST_DECOR_VIEW_ID_3
+            this[1] = TEST_DECOR_VIEW_ID_1
+        })
+
+        Assert.assertNotNull(overlay.getView(TEST_DECOR_VIEW_ID_1))
+        Assert.assertNotNull(overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_1))
+        Assert.assertNull(overlay.getView(TEST_DECOR_VIEW_ID_2))
+        Assert.assertNull(overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_2))
+    }
+
+    @Test
+    fun testHasSameProviders() {
+        Assert.assertTrue(overlay.hasSameProviders(emptyList()))
+        Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider1)))
+        Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2)))
+        Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2, decorProvider1)))
+
+        overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0)
+        Assert.assertFalse(overlay.hasSameProviders(emptyList()))
+        Assert.assertTrue(overlay.hasSameProviders(listOf(decorProvider1)))
+        Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2)))
+        Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2, decorProvider1)))
+
+        overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0)
+        Assert.assertFalse(overlay.hasSameProviders(emptyList()))
+        Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider1)))
+        Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2)))
+        Assert.assertTrue(overlay.hasSameProviders(listOf(decorProvider2, decorProvider1)))
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
index bac0817..171b767 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
@@ -18,7 +18,6 @@
 
 import android.content.res.Resources
 import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper.RunWithLooper
 import android.view.DisplayCutout
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
@@ -32,7 +31,6 @@
 import org.mockito.Mockito.`when` as whenever
 
 @RunWith(AndroidTestingRunner::class)
-@RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class PrivacyDotDecorProviderFactoryTest : SysuiTestCase() {
     private lateinit var mPrivacyDotDecorProviderFactory: PrivacyDotDecorProviderFactory
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt
new file mode 100644
index 0000000..621bcf6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.decor
+
+import android.testing.AndroidTestingRunner
+import android.util.Size
+import android.view.DisplayCutout
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.spy
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class RoundedCornerDecorProviderFactoryTest : SysuiTestCase() {
+
+    @Mock private lateinit var roundedCornerResDelegate: RoundedCornerResDelegate
+    private lateinit var roundedCornerDecorProviderFactory: RoundedCornerDecorProviderFactory
+
+    @Before
+    fun setUp() {
+        roundedCornerResDelegate = spy(RoundedCornerResDelegate(mContext.resources, null))
+    }
+
+    @Test
+    fun testNoRoundedCorners() {
+        Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).topRoundedSize
+        Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).bottomRoundedSize
+        Mockito.doReturn(false).`when`(roundedCornerResDelegate).isMultipleRadius
+
+        roundedCornerDecorProviderFactory =
+                RoundedCornerDecorProviderFactory(roundedCornerResDelegate)
+
+        Assert.assertEquals(false, roundedCornerDecorProviderFactory.hasProviders)
+        Assert.assertEquals(0, roundedCornerDecorProviderFactory.providers.size)
+    }
+
+    @Test
+    fun testHasRoundedCornersIfTopWidthLargerThan0() {
+        Mockito.doReturn(Size(1, 0)).`when`(roundedCornerResDelegate).topRoundedSize
+        Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).bottomRoundedSize
+        Mockito.doReturn(false).`when`(roundedCornerResDelegate).isMultipleRadius
+
+        roundedCornerDecorProviderFactory =
+                RoundedCornerDecorProviderFactory(roundedCornerResDelegate)
+
+        Assert.assertEquals(true, roundedCornerDecorProviderFactory.hasProviders)
+        roundedCornerDecorProviderFactory.providers.let { providers ->
+            Assert.assertEquals(2, providers.size)
+            Assert.assertEquals(1, providers.count {
+                ((it.viewId == R.id.rounded_corner_top_left)
+                        and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP)
+                        and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT))
+            })
+            Assert.assertEquals(1, providers.count {
+                ((it.viewId == R.id.rounded_corner_top_right)
+                        and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP)
+                        and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT))
+            })
+        }
+    }
+
+    @Test
+    fun testHasRoundedCornersIfBottomWidthLargerThan0() {
+        Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).topRoundedSize
+        Mockito.doReturn(Size(1, 1)).`when`(roundedCornerResDelegate).bottomRoundedSize
+        Mockito.doReturn(false).`when`(roundedCornerResDelegate).isMultipleRadius
+
+        roundedCornerDecorProviderFactory =
+                RoundedCornerDecorProviderFactory(roundedCornerResDelegate)
+
+        Assert.assertEquals(true, roundedCornerDecorProviderFactory.hasProviders)
+        roundedCornerDecorProviderFactory.providers.let { providers ->
+            Assert.assertEquals(2, providers.size)
+            Assert.assertEquals(1, providers.count {
+                ((it.viewId == R.id.rounded_corner_bottom_left)
+                        and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM)
+                        and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT))
+            })
+            Assert.assertEquals(1, providers.count {
+                ((it.viewId == R.id.rounded_corner_bottom_right)
+                        and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM)
+                        and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT))
+            })
+        }
+    }
+
+    @Test
+    fun test4CornerDecorProvidersInfo() {
+        Mockito.doReturn(Size(10, 10)).`when`(roundedCornerResDelegate).topRoundedSize
+        Mockito.doReturn(Size(10, 10)).`when`(roundedCornerResDelegate).bottomRoundedSize
+        Mockito.doReturn(true).`when`(roundedCornerResDelegate).isMultipleRadius
+
+        roundedCornerDecorProviderFactory =
+                RoundedCornerDecorProviderFactory(roundedCornerResDelegate)
+
+        Assert.assertEquals(true, roundedCornerDecorProviderFactory.hasProviders)
+        roundedCornerDecorProviderFactory.providers.let { providers ->
+            Assert.assertEquals(4, providers.size)
+            Assert.assertEquals(1, providers.count {
+                ((it.viewId == R.id.rounded_corner_top_left)
+                        and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP)
+                        and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT))
+            })
+            Assert.assertEquals(1, providers.count {
+                ((it.viewId == R.id.rounded_corner_top_right)
+                        and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP)
+                        and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT))
+            })
+            Assert.assertEquals(1, providers.count {
+                ((it.viewId == R.id.rounded_corner_bottom_left)
+                        and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM)
+                        and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT))
+            })
+            Assert.assertEquals(1, providers.count {
+                ((it.viewId == R.id.rounded_corner_bottom_right)
+                        and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM)
+                        and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT))
+            })
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
index 2effaec..1fec380 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
@@ -45,7 +45,7 @@
     }
 
     @Test
-    fun testReloadAllAndDefaultRadius() {
+    fun testUpdateDisplayUniqueId() {
         mContext.orCreateTestableResources.addOverrides(
                 mockTypeArray = mockTypedArray,
                 radius = 3,
@@ -65,7 +65,34 @@
                 radiusTop = 6,
                 radiusBottom = 0)
 
-        roundedCornerResDelegate.reloadAll("test")
+        roundedCornerResDelegate.updateDisplayUniqueId("test", null)
+
+        assertEquals(Size(6, 6), roundedCornerResDelegate.topRoundedSize)
+        assertEquals(Size(5, 5), roundedCornerResDelegate.bottomRoundedSize)
+    }
+
+    @Test
+    fun testNotUpdateDisplayUniqueIdButChangeRefreshToken() {
+        mContext.orCreateTestableResources.addOverrides(
+                mockTypeArray = mockTypedArray,
+                radius = 3,
+                radiusTop = 0,
+                radiusBottom = 4,
+                multipleRadius = false)
+
+        roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null)
+
+        assertEquals(Size(3, 3), roundedCornerResDelegate.topRoundedSize)
+        assertEquals(Size(4, 4), roundedCornerResDelegate.bottomRoundedSize)
+        assertEquals(false, roundedCornerResDelegate.isMultipleRadius)
+
+        mContext.orCreateTestableResources.addOverrides(
+                mockTypeArray = mockTypedArray,
+                radius = 5,
+                radiusTop = 6,
+                radiusBottom = 0)
+
+        roundedCornerResDelegate.updateDisplayUniqueId(null, 1)
 
         assertEquals(Size(6, 6), roundedCornerResDelegate.topRoundedSize)
         assertEquals(Size(5, 5), roundedCornerResDelegate.bottomRoundedSize)
@@ -82,11 +109,21 @@
         roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null)
 
         val factor = 5
-        roundedCornerResDelegate.updateTuningSizeFactor(factor)
+        roundedCornerResDelegate.updateTuningSizeFactor(factor, 1)
         val length = (factor * mContext.resources.displayMetrics.density).toInt()
 
         assertEquals(Size(length, length), roundedCornerResDelegate.topRoundedSize)
         assertEquals(Size(length, length), roundedCornerResDelegate.bottomRoundedSize)
+
+        mContext.orCreateTestableResources.addOverrides(
+                mockTypeArray = mockTypedArray,
+                radiusTop = 1,
+                radiusBottom = 2,
+                multipleRadius = false)
+        roundedCornerResDelegate.updateTuningSizeFactor(null, 2)
+
+        assertEquals(Size(1, 1), roundedCornerResDelegate.topRoundedSize)
+        assertEquals(Size(2, 2), roundedCornerResDelegate.bottomRoundedSize)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index 6453c20..d70467d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -177,7 +177,7 @@
 
         final float bouncerHideAmount = 0.05f;
         final float scaledFraction =
-                BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(bouncerHideAmount);
+                BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(bouncerHideAmount);
 
         bouncerExpansionCaptor.getValue().onExpansionChanged(bouncerHideAmount);
         verify(mBlurUtils).blurRadiusOfRatio(1 - scaledFraction);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/AnimationBindHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/AnimationBindHandlerTest.kt
new file mode 100644
index 0000000..e4cab18
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/AnimationBindHandlerTest.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media
+
+import org.mockito.Mockito.`when` as whenever
+import android.graphics.drawable.Animatable2
+import android.graphics.drawable.Drawable
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import com.android.systemui.SysuiTestCase
+import junit.framework.Assert.assertTrue
+import junit.framework.Assert.assertFalse
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.times
+import org.mockito.Mockito.never
+import org.mockito.junit.MockitoJUnit
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class AnimationBindHandlerTest : SysuiTestCase() {
+
+    private interface Callback : () -> Unit
+    private abstract class AnimatedDrawable : Drawable(), Animatable2
+    private lateinit var handler: AnimationBindHandler
+
+    @Mock private lateinit var animatable: AnimatedDrawable
+    @Mock private lateinit var animatable2: AnimatedDrawable
+    @Mock private lateinit var callback: Callback
+
+    @JvmField @Rule val mockito = MockitoJUnit.rule()
+
+    @Before
+    fun setUp() {
+        handler = AnimationBindHandler()
+    }
+
+    @After
+    fun tearDown() {}
+
+    @Test
+    fun registerNoAnimations_executeCallbackImmediately() {
+        handler.tryExecute(callback)
+        verify(callback).invoke()
+    }
+
+    @Test
+    fun registerStoppedAnimations_executeCallbackImmediately() {
+        whenever(animatable.isRunning).thenReturn(false)
+        whenever(animatable2.isRunning).thenReturn(false)
+
+        handler.tryExecute(callback)
+        verify(callback).invoke()
+    }
+
+    @Test
+    fun registerRunningAnimations_executeCallbackDelayed() {
+        whenever(animatable.isRunning).thenReturn(true)
+        whenever(animatable2.isRunning).thenReturn(true)
+
+        handler.tryRegister(animatable)
+        handler.tryRegister(animatable2)
+        handler.tryExecute(callback)
+
+        verify(callback, never()).invoke()
+
+        whenever(animatable.isRunning).thenReturn(false)
+        handler.onAnimationEnd(animatable)
+        verify(callback, never()).invoke()
+
+        whenever(animatable2.isRunning).thenReturn(false)
+        handler.onAnimationEnd(animatable2)
+        verify(callback, times(1)).invoke()
+    }
+
+    @Test
+    fun repeatedEndCallback_executeSingleCallback() {
+        whenever(animatable.isRunning).thenReturn(true)
+
+        handler.tryRegister(animatable)
+        handler.tryExecute(callback)
+
+        verify(callback, never()).invoke()
+
+        whenever(animatable.isRunning).thenReturn(false)
+        handler.onAnimationEnd(animatable)
+        handler.onAnimationEnd(animatable)
+        handler.onAnimationEnd(animatable)
+        verify(callback, times(1)).invoke()
+    }
+
+    @Test
+    fun registerUnregister_executeImmediately() {
+        whenever(animatable.isRunning).thenReturn(true)
+
+        handler.tryRegister(animatable)
+        handler.unregisterAll()
+        handler.tryExecute(callback)
+
+        verify(callback).invoke()
+    }
+
+    @Test
+    fun updateRebindId_returnsAsExpected() {
+        // Previous or current call is null, returns true
+        assertTrue(handler.updateRebindId(null))
+        assertTrue(handler.updateRebindId(null))
+        assertTrue(handler.updateRebindId(null))
+        assertTrue(handler.updateRebindId(10))
+        assertTrue(handler.updateRebindId(null))
+        assertTrue(handler.updateRebindId(20))
+
+        // Different integer from prevoius, returns true
+        assertTrue(handler.updateRebindId(10))
+        assertTrue(handler.updateRebindId(20))
+
+        // Matches previous call, returns false
+        assertFalse(handler.updateRebindId(20))
+        assertFalse(handler.updateRebindId(20))
+        assertTrue(handler.updateRebindId(10))
+        assertFalse(handler.updateRebindId(10))
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt
new file mode 100644
index 0000000..86527d9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media
+
+import org.mockito.Mockito.`when` as whenever
+import android.animation.ValueAnimator
+import android.graphics.Color
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.monet.ColorScheme
+import junit.framework.Assert.assertEquals
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+private const val DEFAULT_COLOR = Color.RED
+private const val TARGET_COLOR = Color.BLUE
+private const val BG_COLOR = Color.GREEN
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class ColorSchemeTransitionTest : SysuiTestCase() {
+
+    private interface ExtractCB : (ColorScheme) -> Int
+    private interface ApplyCB : (Int) -> Unit
+    private lateinit var colorTransition: ColorTransition
+    private lateinit var colorSchemeTransition: ColorSchemeTransition
+
+    @Mock private lateinit var mockTransition: ColorTransition
+    @Mock private lateinit var valueAnimator: ValueAnimator
+    @Mock private lateinit var colorScheme: ColorScheme
+    @Mock private lateinit var extractColor: ExtractCB
+    @Mock private lateinit var applyColor: ApplyCB
+
+    private lateinit var transitionFactory: ColorTransitionFactory
+    @Mock private lateinit var mediaViewHolder: MediaViewHolder
+
+    @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
+
+    @Before
+    fun setUp() {
+        transitionFactory = { default, extractColor, applyColor -> mockTransition }
+        whenever(extractColor.invoke(colorScheme)).thenReturn(TARGET_COLOR)
+
+        colorSchemeTransition = ColorSchemeTransition(context,
+                BG_COLOR, mediaViewHolder, transitionFactory)
+
+        colorTransition = object : ColorTransition(DEFAULT_COLOR, extractColor, applyColor) {
+            override fun buildAnimator(): ValueAnimator {
+                return valueAnimator
+            }
+        }
+    }
+
+    @After
+    fun tearDown() {}
+
+    @Test
+    fun testColorTransition_nullColorScheme_keepsDefault() {
+        colorTransition.updateColorScheme(null)
+        verify(applyColor, times(1)).invoke(DEFAULT_COLOR)
+        verify(valueAnimator, never()).start()
+        assertEquals(DEFAULT_COLOR, colorTransition.sourceColor)
+        assertEquals(DEFAULT_COLOR, colorTransition.targetColor)
+    }
+
+    @Test
+    fun testColorTransition_newColor_startsAnimation() {
+        colorTransition.updateColorScheme(colorScheme)
+        verify(applyColor, times(1)).invoke(DEFAULT_COLOR)
+        verify(valueAnimator, times(1)).start()
+        assertEquals(DEFAULT_COLOR, colorTransition.sourceColor)
+        assertEquals(TARGET_COLOR, colorTransition.targetColor)
+    }
+
+    @Test
+    fun testColorTransition_sameColor_noAnimation() {
+        whenever(extractColor.invoke(colorScheme)).thenReturn(DEFAULT_COLOR)
+        colorTransition.updateColorScheme(colorScheme)
+        verify(valueAnimator, never()).start()
+        assertEquals(DEFAULT_COLOR, colorTransition.sourceColor)
+        assertEquals(DEFAULT_COLOR, colorTransition.targetColor)
+    }
+
+    @Test
+    fun testColorTransition_colorAnimation_startValues() {
+        val expectedColor = DEFAULT_COLOR
+        whenever(valueAnimator.animatedFraction).thenReturn(0f)
+        colorTransition.updateColorScheme(colorScheme)
+        colorTransition.onAnimationUpdate(valueAnimator)
+
+        assertEquals(expectedColor, colorTransition.currentColor)
+        assertEquals(expectedColor, colorTransition.sourceColor)
+        verify(applyColor, times(2)).invoke(expectedColor) // applied once in constructor
+    }
+
+    @Test
+    fun testColorTransition_colorAnimation_endValues() {
+        val expectedColor = TARGET_COLOR
+        whenever(valueAnimator.animatedFraction).thenReturn(1f)
+        colorTransition.updateColorScheme(colorScheme)
+        colorTransition.onAnimationUpdate(valueAnimator)
+
+        assertEquals(expectedColor, colorTransition.currentColor)
+        assertEquals(expectedColor, colorTransition.targetColor)
+        verify(applyColor).invoke(expectedColor)
+    }
+
+    @Test
+    fun testColorTransition_colorAnimation_interpolatedMidpoint() {
+        val expectedColor = Color.rgb(186, 0, 186)
+        whenever(valueAnimator.animatedFraction).thenReturn(0.5f)
+        colorTransition.updateColorScheme(colorScheme)
+        colorTransition.onAnimationUpdate(valueAnimator)
+
+        assertEquals(expectedColor, colorTransition.currentColor)
+        verify(applyColor).invoke(expectedColor)
+    }
+
+    @Test
+    fun testColorSchemeTransition_update() {
+        colorSchemeTransition.updateColorScheme(colorScheme)
+        verify(mockTransition, times(6)).updateColorScheme(colorScheme)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 21fabeb..1bc8881 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.media
 
+import android.animation.Animator
+import android.animation.AnimatorSet
 import android.app.PendingIntent
 import android.app.smartspace.SmartspaceAction
 import android.content.Context
@@ -39,6 +41,7 @@
 import android.testing.TestableLooper
 import android.view.View
 import android.view.ViewGroup
+import android.view.animation.Interpolator
 import android.widget.FrameLayout
 import android.widget.ImageButton
 import android.widget.ImageView
@@ -58,6 +61,7 @@
 import com.android.systemui.util.animation.TransitionLayout
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.KotlinArgumentCaptor
+import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.withArgCaptor
 import com.android.systemui.util.time.FakeSystemClock
@@ -140,6 +144,7 @@
     private lateinit var actionsTopBarrier: Barrier
     @Mock private lateinit var longPressText: TextView
     @Mock private lateinit var handler: Handler
+    @Mock private lateinit var mockAnimator: AnimatorSet
     private lateinit var settings: ImageButton
     private lateinit var cancel: View
     private lateinit var cancelText: TextView
@@ -181,7 +186,7 @@
         whenever(packageManager.getApplicationLabel(any())).thenReturn(PACKAGE)
         context.setMockPackageManager(packageManager)
 
-        player = MediaControlPanel(
+        player = object : MediaControlPanel(
             context,
             bgExecutor,
             mainExecutor,
@@ -194,8 +199,15 @@
             mediaCarouselController,
             falsingManager,
             clock,
-            logger
-        )
+            logger) {
+                override fun loadAnimator(
+                    animId: Int,
+                    otionInterpolator: Interpolator,
+                    vararg targets: View
+                ): AnimatorSet {
+                    return mockAnimator
+                }
+            }
 
         initMediaViewHolderMocks()
 
@@ -528,7 +540,7 @@
         val icon = context.getDrawable(android.R.drawable.ic_media_play)
         val semanticActions = MediaButton(
             prevOrCustom = MediaAction(icon, {}, "prev", null),
-            nextOrCustom = MediaAction(icon, {}, "next", null),
+            nextOrCustom = MediaAction(icon, {}, "next", null)
         )
         val state = mediaData.copy(semanticActions = semanticActions)
 
@@ -562,7 +574,7 @@
         val icon = context.getDrawable(android.R.drawable.ic_media_play)
         val semanticActions = MediaButton(
             prevOrCustom = null,
-            nextOrCustom = MediaAction(icon, {}, "next", null),
+            nextOrCustom = MediaAction(icon, {}, "next", null)
         )
         val state = mediaData.copy(semanticActions = semanticActions)
         player.attachPlayer(viewHolder)
@@ -582,7 +594,7 @@
         val icon = context.getDrawable(android.R.drawable.ic_media_play)
         val semanticActions = MediaButton(
             prevOrCustom = MediaAction(icon, {}, "prev", null),
-            nextOrCustom = null,
+            nextOrCustom = null
         )
         val state = mediaData.copy(semanticActions = semanticActions)
         player.attachPlayer(viewHolder)
@@ -602,7 +614,7 @@
         val icon = context.getDrawable(android.R.drawable.ic_media_play)
         val semanticActions = MediaButton(
             prevOrCustom = MediaAction(icon, {}, "prev", null),
-            nextOrCustom = MediaAction(icon, {}, "next", null),
+            nextOrCustom = MediaAction(icon, {}, "next", null)
         )
         val state = mediaData.copy(semanticActions = semanticActions)
         player.attachPlayer(viewHolder)
@@ -624,7 +636,7 @@
         val icon = context.getDrawable(android.R.drawable.ic_media_play)
         val semanticActions = MediaButton(
             prevOrCustom = MediaAction(icon, {}, "prev", null),
-            nextOrCustom = MediaAction(icon, {}, "next", null),
+            nextOrCustom = MediaAction(icon, {}, "next", null)
         )
         val state = mediaData.copy(semanticActions = semanticActions)
 
@@ -767,8 +779,53 @@
     fun bindText() {
         player.attachPlayer(viewHolder)
         player.bindPlayer(mediaData, PACKAGE)
+
+        // Capture animation handler
+        val captor = argumentCaptor<Animator.AnimatorListener>()
+        verify(mockAnimator, times(2)).addListener(captor.capture())
+        val handler = captor.value
+
+        // Validate text views unchanged but animation started
+        assertThat(titleText.getText()).isEqualTo("")
+        assertThat(artistText.getText()).isEqualTo("")
+        verify(mockAnimator, times(1)).start()
+
+        // Binding only after animator runs
+        handler.onAnimationEnd(mockAnimator)
         assertThat(titleText.getText()).isEqualTo(TITLE)
         assertThat(artistText.getText()).isEqualTo(ARTIST)
+
+        // Rebinding should not trigger animation
+        player.bindPlayer(mediaData, PACKAGE)
+        verify(mockAnimator, times(1)).start()
+    }
+
+    @Test
+    fun bindTextInterrupted() {
+        val data0 = mediaData.copy(artist = "ARTIST_0")
+        val data1 = mediaData.copy(artist = "ARTIST_1")
+        val data2 = mediaData.copy(artist = "ARTIST_2")
+
+        player.attachPlayer(viewHolder)
+        player.bindPlayer(data0, PACKAGE)
+
+        // Capture animation handler
+        val captor = argumentCaptor<Animator.AnimatorListener>()
+        verify(mockAnimator, times(2)).addListener(captor.capture())
+        val handler = captor.value
+
+        handler.onAnimationEnd(mockAnimator)
+        assertThat(artistText.getText()).isEqualTo("ARTIST_0")
+
+        // Bind trigges new animation
+        player.bindPlayer(data1, PACKAGE)
+        verify(mockAnimator, times(2)).start()
+        whenever(mockAnimator.isRunning()).thenReturn(true)
+
+        // Rebind before animation end binds corrct data
+        player.bindPlayer(data2, PACKAGE)
+        handler.onAnimationEnd(mockAnimator)
+        assertThat(artistText.getText()).isEqualTo("ARTIST_2")
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MetadataAnimationHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MetadataAnimationHandlerTest.kt
new file mode 100644
index 0000000..52cb902
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MetadataAnimationHandlerTest.kt
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media
+
+import org.mockito.Mockito.`when` as whenever
+import android.animation.Animator
+import android.animation.AnimatorSet
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import com.android.systemui.SysuiTestCase
+import junit.framework.Assert.fail
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.times
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.junit.MockitoJUnit
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class MetadataAnimationHandlerTest : SysuiTestCase() {
+
+    private interface Callback : () -> Unit
+    private lateinit var handler: MetadataAnimationHandler
+
+    @Mock private lateinit var animatorSet: AnimatorSet
+    @Mock private lateinit var enterAnimator: Animator
+    @Mock private lateinit var exitAnimator: Animator
+    @Mock private lateinit var postExitCB: Callback
+    @Mock private lateinit var postEnterCB: Callback
+
+    @JvmField @Rule val mockito = MockitoJUnit.rule()
+
+    @Before
+    fun setUp() {
+        handler = object : MetadataAnimationHandler(exitAnimator, enterAnimator) {
+            override fun buildAnimatorSet(exit: Animator, enter: Animator): AnimatorSet {
+                return animatorSet
+            }
+        }
+    }
+
+    @After
+    fun tearDown() {}
+
+    @Test
+    fun firstBind_startsAnimationSet() {
+        val cb = { fail("Unexpected callback") }
+        handler.setNext("data-1", cb, cb)
+
+        verify(animatorSet).start()
+    }
+
+    @Test
+    fun executeAnimationEnd_runsCallacks() {
+        handler.setNext("data-1", postExitCB, postEnterCB)
+        verify(animatorSet, times(1)).start()
+        verify(postExitCB, never()).invoke()
+
+        handler.onAnimationEnd(exitAnimator)
+        verify(animatorSet, times(1)).start()
+        verify(postExitCB, times(1)).invoke()
+        verify(postEnterCB, never()).invoke()
+
+        handler.onAnimationEnd(enterAnimator)
+        verify(animatorSet, times(1)).start()
+        verify(postExitCB, times(1)).invoke()
+        verify(postEnterCB, times(1)).invoke()
+    }
+
+    @Test
+    fun rebindSameData_executesFirstCallback() {
+        val postExitCB2 = mock(Callback::class.java)
+
+        handler.setNext("data-1", postExitCB, postEnterCB)
+        handler.setNext("data-1", postExitCB2, postEnterCB)
+        handler.onAnimationEnd(exitAnimator)
+
+        verify(postExitCB, times(1)).invoke()
+        verify(postExitCB2, never()).invoke()
+        verify(postEnterCB, never()).invoke()
+    }
+
+    @Test
+    fun rebindDifferentData_executesSecondCallback() {
+        val postExitCB2 = mock(Callback::class.java)
+
+        handler.setNext("data-1", postExitCB, postEnterCB)
+        handler.setNext("data-2", postExitCB2, postEnterCB)
+        handler.onAnimationEnd(exitAnimator)
+
+        verify(postExitCB, never()).invoke()
+        verify(postExitCB2, times(1)).invoke()
+        verify(postEnterCB, never()).invoke()
+    }
+
+    @Test
+    fun rebindBeforeEnterComplete_animationRestarts() {
+        val postExitCB2 = mock(Callback::class.java)
+        val postEnterCB2 = mock(Callback::class.java)
+
+        handler.setNext("data-1", postExitCB, postEnterCB)
+        verify(animatorSet, times(1)).start()
+        verify(postExitCB, never()).invoke()
+        verify(postExitCB2, never()).invoke()
+        verify(postEnterCB, never()).invoke()
+        verify(postEnterCB2, never()).invoke()
+
+        whenever(animatorSet.isRunning()).thenReturn(true)
+        handler.onAnimationEnd(exitAnimator)
+        verify(animatorSet, times(1)).start()
+        verify(postExitCB, times(1)).invoke()
+        verify(postExitCB2, never()).invoke()
+        verify(postEnterCB, never()).invoke()
+        verify(postEnterCB2, never()).invoke()
+
+        handler.setNext("data-2", postExitCB2, postEnterCB2)
+        handler.onAnimationEnd(enterAnimator)
+        verify(animatorSet, times(2)).start()
+        verify(postExitCB, times(1)).invoke()
+        verify(postExitCB2, never()).invoke()
+        verify(postEnterCB, never()).invoke()
+        verify(postEnterCB2, never()).invoke()
+
+        handler.onAnimationEnd(exitAnimator)
+        verify(animatorSet, times(2)).start()
+        verify(postExitCB, times(1)).invoke()
+        verify(postExitCB2, times(1)).invoke()
+        verify(postEnterCB, never()).invoke()
+        verify(postEnterCB2, never()).invoke()
+
+        handler.onAnimationEnd(enterAnimator)
+        verify(animatorSet, times(2)).start()
+        verify(postExitCB, times(1)).invoke()
+        verify(postExitCB2, times(1)).invoke()
+        verify(postEnterCB, never()).invoke()
+        verify(postEnterCB2, times(1)).invoke()
+    }
+
+    @Test
+    fun exitAnimationEndMultipleCalls_singleCallbackExecution() {
+        handler.setNext("data-1", postExitCB, postEnterCB)
+        handler.onAnimationEnd(exitAnimator)
+        handler.onAnimationEnd(exitAnimator)
+        handler.onAnimationEnd(exitAnimator)
+
+        verify(postExitCB, times(1)).invoke()
+    }
+
+    @Test
+    fun enterAnimatorEndsWithoutCallback_noAnimatiorStart() {
+        handler.onAnimationEnd(enterAnimator)
+
+        verify(animatorSet, never()).start()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
index c48d846..49be669 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.media
 
+import android.animation.Animator
+import android.animation.ObjectAnimator
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
@@ -43,6 +45,7 @@
     private val enabledHeight = 2
 
     private lateinit var observer: SeekBarObserver
+    @Mock private lateinit var mockSeekbarAnimator: ObjectAnimator
     @Mock private lateinit var mockHolder: MediaViewHolder
     @Mock private lateinit var mockSquigglyProgress: SquigglyProgress
     private lateinit var seekBarView: SeekBar
@@ -66,7 +69,11 @@
         whenever(mockHolder.scrubbingElapsedTimeView).thenReturn(scrubbingElapsedTimeView)
         whenever(mockHolder.scrubbingTotalTimeView).thenReturn(scrubbingTotalTimeView)
 
-        observer = SeekBarObserver(mockHolder)
+        observer = object : SeekBarObserver(mockHolder) {
+            override fun buildResetAnimator(targetTime: Int): Animator {
+                return mockSeekbarAnimator
+            }
+        }
     }
 
     @Test
@@ -189,4 +196,20 @@
         assertThat(scrubbingElapsedTimeView.text).isEqualTo("")
         assertThat(scrubbingTotalTimeView.text).isEqualTo("")
     }
+
+    @Test
+    fun seekBarJumpAnimation() {
+        val data0 = SeekBarViewModel.Progress(true, true, true, false, 4000, 120000)
+        val data1 = SeekBarViewModel.Progress(true, true, true, false, 10, 120000)
+
+        // Set initial position of progress bar
+        observer.onChanged(data0)
+        assertThat(seekBarView.progress).isEqualTo(4000)
+        assertThat(seekBarView.max).isEqualTo(120000)
+
+        // Change to second data & confirm no change to position (due to animation delay)
+        observer.onChanged(data1)
+        assertThat(seekBarView.progress).isEqualTo(4000)
+        verify(mockSeekbarAnimator).start()
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index f5b006d..4a740f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -205,10 +205,9 @@
         when(mNavigationBarView.getAccessibilityButton()).thenReturn(mAccessibilityButton);
         when(mNavigationBarView.getImeSwitchButton()).thenReturn(mImeSwitchButton);
         when(mNavigationBarView.getBackButton()).thenReturn(mBackButton);
-        when(mNavigationBarView.getBarTransitions()).thenReturn(mNavigationBarTransitions);
         when(mNavigationBarView.getRotationButtonController())
                 .thenReturn(mRotationButtonController);
-        when(mNavigationBarView.getLightTransitionsController())
+        when(mNavigationBarTransitions.getLightTransitionsController())
                 .thenReturn(mLightBarTransitionsController);
         when(mStatusBarKeyguardViewManager.isNavBarVisible()).thenReturn(true);
         setupSysuiDependency();
@@ -459,6 +458,7 @@
                 mInputMethodManager,
                 mDeadZone,
                 mDeviceConfigProxyFake,
+                mNavigationBarTransitions,
                 Optional.of(mock(BackAnimation.class))));
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
index 6a2a78b..084eca8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
@@ -21,7 +21,6 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
@@ -37,8 +36,8 @@
 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.BarTransitions;
+import com.android.systemui.statusbar.phone.LightBarTransitionsController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import org.junit.Before;
@@ -53,6 +52,10 @@
 public class NavigationBarTransitionsTest extends SysuiTestCase {
 
     @Mock
+    LightBarTransitionsController.Factory mLightBarTransitionsFactory;
+    @Mock
+    LightBarTransitionsController mLightBarTransitions;
+    @Mock
     EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory;
     @Mock
     EdgeBackGestureHandler mEdgeBackGestureHandler;
@@ -76,10 +79,11 @@
                 .when(mDependency.injectMockDependency(NavigationModeController.class))
                 .getCurrentUserContext();
 
+        when(mLightBarTransitionsFactory.create(any())).thenReturn(mLightBarTransitions);
         NavigationBarView navBar = spy(new NavigationBarView(mContext, null));
         when(navBar.getCurrentView()).thenReturn(navBar);
         when(navBar.findViewById(anyInt())).thenReturn(navBar);
-        mTransitions = new NavigationBarTransitions(navBar, mock(CommandQueue.class));
+        mTransitions = new NavigationBarTransitions(navBar, mLightBarTransitionsFactory);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
index b4b4597..e6b960d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
@@ -33,8 +33,12 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.app.people.ConversationStatus;
@@ -44,6 +48,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.UserHandle;
@@ -581,6 +586,27 @@
     }
 
     @Test
+    public void testCreateRemoteViewsWithNotificationContent() throws Exception {
+        PeopleTileViewHelper helper = spy(getPeopleTileViewHelper(PERSON_TILE));
+        doReturn(new BitmapDrawable()).when(helper).resolveImage(any(), any());
+        RemoteViews views = helper.getViews();
+        View result = views.apply(mContext, null);
+
+        assertEquals(View.VISIBLE, result.findViewById(R.id.image).getVisibility());
+    }
+
+    @Test
+    public void testCreateRemoteViewsWithInvalidNotificationContent() throws Exception {
+        PeopleTileViewHelper helper = spy(getPeopleTileViewHelper(PERSON_TILE));
+        doThrow(SecurityException.class).when(helper).resolveImage(any(), any());
+        RemoteViews views = helper.getViews();
+        View result = views.apply(mContext, null);
+
+        assertEquals(View.GONE, result.findViewById(R.id.image).getVisibility());
+        assertEquals(View.VISIBLE, result.findViewById(R.id.text_content).getVisibility());
+    }
+
+    @Test
     public void testCreateRemoteViewsWithUserQuieted() {
         PeopleSpaceTile tile = PERSON_TILE.toBuilder()
                 .setIsUserQuieted(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 829445e..4fbdb7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -214,7 +214,7 @@
 
         assertThat(mQsFragmentView.getAlpha())
                 .isEqualTo(
-                        BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(
+                        BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(
                                 transitionProgress));
     }
 
@@ -315,6 +315,24 @@
         verify(mQuickQSPanelController).setCollapseExpandAction(action);
     }
 
+    @Test
+    public void setOverScrollAmount_setsTranslationOnView() {
+        QSFragment fragment = resumeAndGetFragment();
+
+        fragment.setOverScrollAmount(123);
+
+        assertThat(mQsFragmentView.getTranslationY()).isEqualTo(123);
+    }
+
+    @Test
+    public void setOverScrollAmount_beforeViewCreated_translationIsNotSet() {
+        QSFragment fragment = getFragment();
+
+        fragment.setOverScrollAmount(123);
+
+        assertThat(mQsFragmentView.getTranslationY()).isEqualTo(0);
+    }
+
     @Override
     protected Fragment instantiate(Context context, String className, Bundle arguments) {
         MockitoAnnotations.initMocks(this);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 324f0ac..b1f1075 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -44,12 +44,15 @@
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
+import android.app.PendingIntent;
+import android.app.Person;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
+import android.telecom.TelecomManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -116,11 +119,15 @@
     private ChannelEditorDialogController mChannelEditorDialogController;
     @Mock
     private AssistantFeedbackController mAssistantFeedbackController;
+    @Mock
+    private TelecomManager mTelecomManager;
 
     @Before
     public void setUp() throws Exception {
         mTestableLooper = TestableLooper.get(this);
 
+        mContext.addMockSystemService(TelecomManager.class, mTelecomManager);
+
         mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
         mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
         // Inflate the layout
@@ -161,7 +168,7 @@
                 IMPORTANCE_LOW);
         mDefaultNotificationChannelSet.add(mDefaultNotificationChannel);
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
-                new Notification(), UserHandle.CURRENT, null, 0);
+                new Notification(), UserHandle.getUserHandleForUid(TEST_UID), null, 0);
         mEntry = new NotificationEntryBuilder().setSbn(mSbn).build();
         when(mAssistantFeedbackController.isFeedbackEnabled()).thenReturn(false);
         when(mAssistantFeedbackController.getInlineDescriptionResource(any()))
@@ -632,6 +639,92 @@
     }
 
     @Test
+    public void testBindNotification_whenCurrentlyInCall() throws Exception {
+        when(mMockINotificationManager.isInCall(anyString(), anyInt())).thenReturn(true);
+
+        Person person = new Person.Builder()
+                .setName("caller")
+                .build();
+        Notification.Builder nb = new Notification.Builder(
+                mContext, mNotificationChannel.getId())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(Notification.CallStyle.forOngoingCall(
+                        person, mock(PendingIntent.class)))
+                .setFullScreenIntent(mock(PendingIntent.class), true)
+                .addAction(new Notification.Action.Builder(null, "test", null).build());
+
+        mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
+                nb.build(), UserHandle.getUserHandleForUid(TEST_UID), null, 0);
+        mEntry.setSbn(mSbn);
+        mNotificationInfo.bindNotification(
+                mMockPackageManager,
+                mMockINotificationManager,
+                mOnUserInteractionCallback,
+                mChannelEditorDialogController,
+                TEST_PACKAGE_NAME,
+                mNotificationChannel,
+                mNotificationChannelSet,
+                mEntry,
+                null,
+                null,
+                mUiEventLogger,
+                true,
+                false,
+                true,
+                mAssistantFeedbackController);
+        final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_call_text);
+        assertEquals(View.VISIBLE, view.getVisibility());
+        assertEquals(mContext.getString(R.string.notification_unblockable_call_desc),
+                view.getText());
+        assertEquals(GONE,
+                mNotificationInfo.findViewById(R.id.interruptiveness_settings).getVisibility());
+        assertEquals(GONE,
+                mNotificationInfo.findViewById(R.id.non_configurable_text).getVisibility());
+    }
+
+    @Test
+    public void testBindNotification_whenCurrentlyInCall_notCall() throws Exception {
+        when(mMockINotificationManager.isInCall(anyString(), anyInt())).thenReturn(true);
+
+        Person person = new Person.Builder()
+                .setName("caller")
+                .build();
+        Notification.Builder nb = new Notification.Builder(
+                mContext, mNotificationChannel.getId())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setFullScreenIntent(mock(PendingIntent.class), true)
+                .addAction(new Notification.Action.Builder(null, "test", null).build());
+
+        mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
+                nb.build(), UserHandle.getUserHandleForUid(TEST_UID), null, 0);
+        mEntry.setSbn(mSbn);
+        mNotificationInfo.bindNotification(
+                mMockPackageManager,
+                mMockINotificationManager,
+                mOnUserInteractionCallback,
+                mChannelEditorDialogController,
+                TEST_PACKAGE_NAME,
+                mNotificationChannel,
+                mNotificationChannelSet,
+                mEntry,
+                null,
+                null,
+                mUiEventLogger,
+                true,
+                false,
+                true,
+                mAssistantFeedbackController);
+        assertEquals(GONE,
+                mNotificationInfo.findViewById(R.id.non_configurable_call_text).getVisibility());
+        assertEquals(VISIBLE,
+                mNotificationInfo.findViewById(R.id.interruptiveness_settings).getVisibility());
+        assertEquals(GONE,
+                mNotificationInfo.findViewById(R.id.non_configurable_text).getVisibility());
+    }
+
+    @Test
     public void testBindNotification_automaticIsVisible() throws Exception {
         when(mAssistantFeedbackController.isFeedbackEnabled()).thenReturn(true);
         mNotificationInfo.bindNotification(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
index dfd70a2..968e16a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.stack
 
+import android.annotation.DimenRes
 import android.service.notification.StatusBarNotification
 import android.testing.AndroidTestingRunner
 import android.view.View.VISIBLE
@@ -27,6 +28,7 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.nullable
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -34,8 +36,8 @@
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito.mock
-import org.mockito.MockitoAnnotations
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -49,17 +51,15 @@
 
     private lateinit var sizeCalculator: NotificationStackSizeCalculator
 
+    private val gapHeight = px(R.dimen.notification_section_divider_height)
+    private val dividerHeight = px(R.dimen.notification_divider_height)
+    private val shelfHeight = px(R.dimen.notification_shelf_height)
+    private val rowHeight = px(R.dimen.notification_max_height)
+
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        whenever(stackLayout.calculateGapHeight(nullable(), nullable(), any()))
-            .thenReturn(GAP_HEIGHT)
-        with(testableResources) {
-            addOverride(R.integer.keyguard_max_notification_count, -1)
-            addOverride(R.dimen.notification_divider_height, DIVIDER_HEIGHT.toInt())
-        }
-
         sizeCalculator =
             NotificationStackSizeCalculator(
                 statusBarStateController = sysuiStatusBarStateController,
@@ -68,7 +68,7 @@
 
     @Test
     fun computeMaxKeyguardNotifications_zeroSpace_returnZero() {
-        val rows = listOf(createMockRow(height = ROW_HEIGHT))
+        val rows = listOf(createMockRow(height = rowHeight))
 
         val maxNotifications =
             computeMaxKeyguardNotifications(rows, availableSpace = 0f, shelfHeight = 0f)
@@ -87,105 +87,78 @@
     }
 
     @Test
-    fun computeMaxKeyguardNotifications_spaceForOne_returnsOne() {
-        val rowHeight = ROW_HEIGHT
-        val totalSpaceForEachRow = GAP_HEIGHT + rowHeight
-        val shelfHeight =
-            totalSpaceForEachRow / 2 // In this way shelf absence will not leave room for another.
-        val spaceForOne = totalSpaceForEachRow
-        val rows =
-            listOf(
-                createMockRow(rowHeight),
-                createMockRow(rowHeight))
+    fun computeMaxKeyguardNotifications_spaceForOneAndShelf_returnsOne() {
+        setGapHeight(gapHeight)
+        val shelfHeight = rowHeight / 2 // Shelf absence won't leave room for another row.
+        val availableSpace =
+            listOf(rowHeight + dividerHeight, gapHeight + dividerHeight + shelfHeight).sum()
+        val rows = listOf(createMockRow(rowHeight), createMockRow(rowHeight))
 
-        val maxNotifications =
-            computeMaxKeyguardNotifications(
-                rows, availableSpace = spaceForOne, shelfHeight = shelfHeight)
-
-        assertThat(maxNotifications).isEqualTo(1)
-    }
-
-    @Test
-    fun computeMaxKeyguardNotifications_spaceForOne_shelfUsableForLastNotification_returnsTwo() {
-        val rowHeight = ROW_HEIGHT
-        val totalSpaceForEachRow = GAP_HEIGHT + rowHeight
-        val shelfHeight = totalSpaceForEachRow + DIVIDER_HEIGHT
-        val spaceForOne = totalSpaceForEachRow
-        val rows =
-            listOf(
-                createMockRow(rowHeight),
-                createMockRow(rowHeight))
-
-        val maxNotifications =
-            computeMaxKeyguardNotifications(
-                rows, availableSpace = spaceForOne, shelfHeight = shelfHeight)
+        val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight)
 
         assertThat(maxNotifications).isEqualTo(1)
     }
 
     @Test
     fun computeMaxKeyguardNotifications_spaceForTwo_returnsTwo() {
-        val rowHeight = ROW_HEIGHT
-        val totalSpaceForEachRow = GAP_HEIGHT + rowHeight
-        val spaceForTwo = totalSpaceForEachRow * 2 + DIVIDER_HEIGHT
-        val rows =
+        setGapHeight(gapHeight)
+        val shelfHeight = shelfHeight + dividerHeight
+        val availableSpace =
             listOf(
-                createMockRow(rowHeight),
-                createMockRow(rowHeight),
-                createMockRow(rowHeight))
+                    rowHeight + dividerHeight,
+                    gapHeight + rowHeight + dividerHeight,
+                    gapHeight + dividerHeight + shelfHeight)
+                .sum()
+        val rows =
+            listOf(createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight))
 
-        val maxNotifications = computeMaxKeyguardNotifications(rows, spaceForTwo, shelfHeight = 0f)
+        val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight)
 
         assertThat(maxNotifications).isEqualTo(2)
     }
 
     @Test
     fun computeHeight_returnsAtMostSpaceAvailable_withGapBeforeShelf() {
-        val rowHeight = ROW_HEIGHT
-        val shelfHeight = SHELF_HEIGHT
-        val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + DIVIDER_HEIGHT
-        val availableSpace = totalSpaceForEachRow * 2
+        setGapHeight(gapHeight)
+        val shelfHeight = shelfHeight
+        val availableSpace =
+            listOf(
+                    rowHeight + dividerHeight,
+                    gapHeight + rowHeight + dividerHeight,
+                    gapHeight + dividerHeight + shelfHeight)
+                .sum()
 
         // All rows in separate sections (default setup).
         val rows =
-            listOf(
-                createMockRow(rowHeight),
-                createMockRow(rowHeight),
-                createMockRow(rowHeight))
+            listOf(createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight))
 
         val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight)
         assertThat(maxNotifications).isEqualTo(2)
 
-        val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, SHELF_HEIGHT)
-        assertThat(height).isAtMost(availableSpace + GAP_HEIGHT + SHELF_HEIGHT)
+        val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight)
+        assertThat(height).isAtMost(availableSpace)
     }
 
     @Test
-    fun computeHeight_returnsAtMostSpaceAvailable_noGapBeforeShelf() {
-        val rowHeight = ROW_HEIGHT
-        val shelfHeight = SHELF_HEIGHT
-        val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + DIVIDER_HEIGHT
-        val availableSpace = totalSpaceForEachRow * 1
-
+    fun computeHeight_noGapBeforeShelf_returnsAtMostSpaceAvailable() {
         // Both rows are in the same section.
-        whenever(stackLayout.calculateGapHeight(nullable(), nullable(), any()))
-                .thenReturn(0f)
-        val rows =
-                listOf(
-                        createMockRow(rowHeight),
-                        createMockRow(rowHeight))
+        setGapHeight(0f)
+        val rowHeight = rowHeight
+        val shelfHeight = shelfHeight
+        val availableSpace = listOf(rowHeight + dividerHeight, dividerHeight + shelfHeight).sum()
+        val rows = listOf(createMockRow(rowHeight), createMockRow(rowHeight))
 
         val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight)
         assertThat(maxNotifications).isEqualTo(1)
 
-        val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, SHELF_HEIGHT)
-        assertThat(height).isAtMost(availableSpace + SHELF_HEIGHT)
+        val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight)
+        assertThat(height).isAtMost(availableSpace)
     }
 
     private fun computeMaxKeyguardNotifications(
         rows: List<ExpandableView>,
         availableSpace: Float,
-        shelfHeight: Float = SHELF_HEIGHT
+        shelfHeight: Float = this.shelfHeight
     ): Int {
         setupChildren(rows)
         return sizeCalculator.computeMaxKeyguardNotifications(
@@ -204,9 +177,9 @@
         (1..number).map { createMockRow() }.toList()
 
     private fun createMockRow(
-        height: Float = ROW_HEIGHT,
+        height: Float = rowHeight,
         isRemoved: Boolean = false,
-        visibility: Int = VISIBLE,
+        visibility: Int = VISIBLE
     ): ExpandableNotificationRow {
         val row = mock(ExpandableNotificationRow::class.java)
         val entry = mock(NotificationEntry::class.java)
@@ -220,11 +193,12 @@
         return row
     }
 
-    /** Default dimensions for tests that don't overwrite them. */
-    companion object {
-        const val GAP_HEIGHT = 12f
-        const val DIVIDER_HEIGHT = 3f
-        const val SHELF_HEIGHT = 14f
-        const val ROW_HEIGHT = SHELF_HEIGHT * 3
+    private fun setGapHeight(height: Float) {
+        whenever(stackLayout.calculateGapHeight(nullable(), nullable(), any())).thenReturn(height)
+        whenever(stackLayout.calculateGapHeight(nullable(), nullable(), /* visibleIndex= */ eq(0)))
+            .thenReturn(0f)
     }
+
+    private fun px(@DimenRes id: Int): Float =
+        testableResources.resources.getDimensionPixelSize(id).toFloat()
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 509fa3b..69d7932 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -1244,11 +1244,11 @@
 
         float expansion = 0.8f;
         float expectedAlpha =
-                BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(expansion);
+                BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion);
         assertAlphaAfterExpansion(mNotificationsScrim, expectedAlpha, expansion);
 
         expansion = 0.2f;
-        expectedAlpha = BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(expansion);
+        expectedAlpha = BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion);
         assertAlphaAfterExpansion(mNotificationsScrim, expectedAlpha, expansion);
     }
 
@@ -1284,7 +1284,7 @@
         // Verify normal behavior after
         mScrimController.setUnocclusionAnimationRunning(false);
         float expansion = 0.4f;
-        float alpha = 1 - BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(expansion);
+        float alpha = 1 - BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion);
         assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion);
     }
 
@@ -1316,15 +1316,15 @@
         mScrimController.transitionTo(ScrimState.KEYGUARD);
 
         float expansion = 0.8f;
-        float alpha = 1 - BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(expansion);
+        float alpha = 1 - BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion);
         assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion);
 
         expansion = 0.4f;
-        alpha = 1 - BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(expansion);
+        alpha = 1 - BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion);
         assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion);
 
         expansion = 0.2f;
-        alpha = 1 - BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(expansion);
+        alpha = 1 - BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion);
         assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
index 799dafc..e3d2a29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
@@ -28,6 +28,7 @@
 import android.os.Handler
 import android.os.UserHandle
 import android.os.UserManager
+import android.provider.Settings
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.ThreadedRenderer
@@ -51,6 +52,7 @@
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView
 import com.android.systemui.telephony.TelephonyListenerManager
 import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.settings.GlobalSettings
 import com.android.systemui.util.settings.SecureSettings
 import com.android.systemui.util.time.FakeSystemClock
 import org.junit.Assert.assertEquals
@@ -67,6 +69,7 @@
 import org.mockito.Mockito.any
 import org.mockito.Mockito.doNothing
 import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.eq
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
@@ -95,6 +98,7 @@
     @Mock private lateinit var notificationShadeWindowView: NotificationShadeWindowView
     @Mock private lateinit var threadedRenderer: ThreadedRenderer
     @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+    @Mock private lateinit var globalSettings: GlobalSettings
     private lateinit var testableLooper: TestableLooper
     private lateinit var bgExecutor: FakeExecutor
     private lateinit var longRunningExecutor: FakeExecutor
@@ -148,6 +152,22 @@
         `when`(userTracker.userId).thenReturn(ownerId)
         `when`(userTracker.userInfo).thenReturn(ownerInfo)
 
+        `when`(
+            globalSettings.getIntForUser(
+                eq(Settings.Global.ADD_USERS_WHEN_LOCKED),
+                anyInt(),
+                eq(UserHandle.USER_SYSTEM)
+            )
+        ).thenReturn(0)
+
+        `when`(
+            globalSettings.getIntForUser(
+                eq(Settings.Global.USER_SWITCHER_ENABLED),
+                anyInt(),
+                eq(UserHandle.USER_SYSTEM)
+            )
+        ).thenReturn(1)
+
         setupController()
     }
 
@@ -168,6 +188,7 @@
                 falsingManager,
                 telephonyListenerManager,
                 secureSettings,
+                globalSettings,
                 bgExecutor,
                 longRunningExecutor,
                 uiExecutor,
@@ -469,4 +490,43 @@
         // THEN a supervised user can NOT be constructed
         assertFalse(userSwitcherController.canCreateSupervisedUser())
     }
+
+    @Test
+    fun testCannotCreateUserWhenUserSwitcherDisabled() {
+        `when`(
+            globalSettings.getIntForUser(
+                eq(Settings.Global.USER_SWITCHER_ENABLED),
+                anyInt(),
+                eq(UserHandle.USER_SYSTEM)
+            )
+        ).thenReturn(0)
+        setupController()
+        assertFalse(userSwitcherController.canCreateUser())
+    }
+
+    @Test
+    fun testCannotCreateGuestUserWhenUserSwitcherDisabled() {
+        `when`(
+            globalSettings.getIntForUser(
+                eq(Settings.Global.USER_SWITCHER_ENABLED),
+                anyInt(),
+                eq(UserHandle.USER_SYSTEM)
+            )
+        ).thenReturn(0)
+        setupController()
+        assertFalse(userSwitcherController.canCreateGuest(false))
+    }
+
+    @Test
+    fun testCannotCreateSupervisedUserWhenUserSwitcherDisabled() {
+        `when`(
+            globalSettings.getIntForUser(
+                eq(Settings.Global.USER_SWITCHER_ENABLED),
+                anyInt(),
+                eq(UserHandle.USER_SYSTEM)
+            )
+        ).thenReturn(0)
+        setupController()
+        assertFalse(userSwitcherController.canCreateSupervisedUser())
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 5ef1008..562d11a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -69,6 +69,7 @@
 import android.os.SystemClock;
 import android.os.Trace;
 import android.provider.Settings;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
@@ -218,12 +219,6 @@
         @NonNull KeyEventDispatcher getKeyEventDispatcher();
 
         /**
-         * @param windowId The id of the window of interest
-         * @return The magnification spec for the window, or {@code null} if none is available
-         */
-        @Nullable MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId);
-
-        /**
          * @param displayId The display id.
          * @return The current injector of motion events used on the display, if one exists.
          */
@@ -557,7 +552,8 @@
         final int resolvedWindowId;
         RemoteAccessibilityConnection connection;
         Region partialInteractiveRegion = Region.obtain();
-        MagnificationSpec spec;
+        final MagnificationSpec spec;
+        final float[] transformMatrix;
         synchronized (mLock) {
             mUsesAccessibilityCache = true;
             if (!hasRightsToCurrentUserLocked()) {
@@ -581,7 +577,10 @@
                 partialInteractiveRegion.recycle();
                 partialInteractiveRegion = null;
             }
-            spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId);
+            final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
+                    getTransformMatrixAndSpecLocked(resolvedWindowId);
+            transformMatrix = transformMatrixAndSpec.first;
+            spec = transformMatrixAndSpec.second;
         }
         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
             return null;
@@ -594,12 +593,12 @@
             logTraceIntConn("findAccessibilityNodeInfosByViewId",
                     accessibilityNodeId + ";" + viewIdResName + ";" + partialInteractiveRegion + ";"
                     + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid
-                    + ";" + interrogatingTid + ";" + spec);
+                    + ";" + interrogatingTid + ";" + spec + ";" + Arrays.toString(transformMatrix));
         }
         try {
             connection.getRemote().findAccessibilityNodeInfosByViewId(accessibilityNodeId,
                     viewIdResName, partialInteractiveRegion, interactionId, callback, mFetchFlags,
-                    interrogatingPid, interrogatingTid, spec);
+                    interrogatingPid, interrogatingTid, spec, transformMatrix);
             return mSecurityPolicy.computeValidReportedPackages(
                     connection.getPackageName(), connection.getUid());
         } catch (RemoteException re) {
@@ -630,7 +629,8 @@
         final int resolvedWindowId;
         RemoteAccessibilityConnection connection;
         Region partialInteractiveRegion = Region.obtain();
-        MagnificationSpec spec;
+        final MagnificationSpec spec;
+        final float [] transformMatrix;
         synchronized (mLock) {
             mUsesAccessibilityCache = true;
             if (!hasRightsToCurrentUserLocked()) {
@@ -654,7 +654,10 @@
                 partialInteractiveRegion.recycle();
                 partialInteractiveRegion = null;
             }
-            spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId);
+            final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
+                    getTransformMatrixAndSpecLocked(resolvedWindowId);
+            transformMatrix = transformMatrixAndSpec.first;
+            spec = transformMatrixAndSpec.second;
         }
         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
             return null;
@@ -667,12 +670,12 @@
             logTraceIntConn("findAccessibilityNodeInfosByText",
                     accessibilityNodeId + ";" + text + ";" + partialInteractiveRegion + ";"
                     + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid
-                    + ";" + interrogatingTid + ";" + spec);
+                    + ";" + interrogatingTid + ";" + spec + ";" + Arrays.toString(transformMatrix));
         }
         try {
             connection.getRemote().findAccessibilityNodeInfosByText(accessibilityNodeId,
                     text, partialInteractiveRegion, interactionId, callback, mFetchFlags,
-                    interrogatingPid, interrogatingTid, spec);
+                    interrogatingPid, interrogatingTid, spec, transformMatrix);
             return mSecurityPolicy.computeValidReportedPackages(
                     connection.getPackageName(), connection.getUid());
         } catch (RemoteException re) {
@@ -704,7 +707,8 @@
         final int resolvedWindowId;
         RemoteAccessibilityConnection connection;
         Region partialInteractiveRegion = Region.obtain();
-        MagnificationSpec spec;
+        final MagnificationSpec spec;
+        final float[] transformMatrix;
         synchronized (mLock) {
             mUsesAccessibilityCache = true;
             if (!hasRightsToCurrentUserLocked()) {
@@ -728,7 +732,10 @@
                 partialInteractiveRegion.recycle();
                 partialInteractiveRegion = null;
             }
-            spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId);
+            final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
+                    getTransformMatrixAndSpecLocked(resolvedWindowId);
+            transformMatrix = transformMatrixAndSpec.first;
+            spec = transformMatrixAndSpec.second;
         }
         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
             return null;
@@ -741,12 +748,14 @@
             logTraceIntConn("findAccessibilityNodeInfoByAccessibilityId",
                     accessibilityNodeId + ";" + partialInteractiveRegion + ";" + interactionId + ";"
                     + callback + ";" + (mFetchFlags | flags) + ";" + interrogatingPid + ";"
-                    + interrogatingTid + ";" + spec + ";" + arguments);
+                            + interrogatingTid + ";" + spec + ";" + Arrays.toString(transformMatrix)
+                            + ";" + arguments);
         }
         try {
             connection.getRemote().findAccessibilityNodeInfoByAccessibilityId(
                     accessibilityNodeId, partialInteractiveRegion, interactionId, callback,
-                    mFetchFlags | flags, interrogatingPid, interrogatingTid, spec, arguments);
+                    mFetchFlags | flags, interrogatingPid, interrogatingTid, spec, transformMatrix,
+                    arguments);
             return mSecurityPolicy.computeValidReportedPackages(
                     connection.getPackageName(), connection.getUid());
         } catch (RemoteException re) {
@@ -778,7 +787,8 @@
         final int resolvedWindowId;
         RemoteAccessibilityConnection connection;
         Region partialInteractiveRegion = Region.obtain();
-        MagnificationSpec spec;
+        final MagnificationSpec spec;
+        final float[] transformMatrix;
         synchronized (mLock) {
             if (!hasRightsToCurrentUserLocked()) {
                 return null;
@@ -802,7 +812,10 @@
                 partialInteractiveRegion.recycle();
                 partialInteractiveRegion = null;
             }
-            spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId);
+            final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
+                    getTransformMatrixAndSpecLocked(resolvedWindowId);
+            transformMatrix = transformMatrixAndSpec.first;
+            spec = transformMatrixAndSpec.second;
         }
         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
             return null;
@@ -815,12 +828,13 @@
             logTraceIntConn("findFocus",
                     accessibilityNodeId + ";" + focusType + ";" + partialInteractiveRegion + ";"
                     + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid
-                    + ";" + interrogatingTid + ";" + spec);
+                            + ";" + interrogatingTid + ";" + spec + ";"
+                            + Arrays.toString(transformMatrix));
         }
         try {
             connection.getRemote().findFocus(accessibilityNodeId, focusType,
                     partialInteractiveRegion, interactionId, callback, mFetchFlags,
-                    interrogatingPid, interrogatingTid, spec);
+                    interrogatingPid, interrogatingTid, spec, transformMatrix);
             return mSecurityPolicy.computeValidReportedPackages(
                     connection.getPackageName(), connection.getUid());
         } catch (RemoteException re) {
@@ -852,7 +866,8 @@
         final int resolvedWindowId;
         RemoteAccessibilityConnection connection;
         Region partialInteractiveRegion = Region.obtain();
-        MagnificationSpec spec;
+        final MagnificationSpec spec;
+        final float[] transformMatrix;
         synchronized (mLock) {
             if (!hasRightsToCurrentUserLocked()) {
                 return null;
@@ -875,7 +890,11 @@
                 partialInteractiveRegion.recycle();
                 partialInteractiveRegion = null;
             }
-            spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId);
+
+            final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
+                    getTransformMatrixAndSpecLocked(resolvedWindowId);
+            transformMatrix = transformMatrixAndSpec.first;
+            spec = transformMatrixAndSpec.second;
         }
         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
             return null;
@@ -888,12 +907,13 @@
             logTraceIntConn("focusSearch",
                     accessibilityNodeId + ";" + direction + ";" + partialInteractiveRegion
                     + ";" + interactionId + ";" + callback + ";" + mFetchFlags + ";"
-                    + interrogatingPid + ";" + interrogatingTid + ";" + spec);
+                            + interrogatingPid + ";" + interrogatingTid + ";" + spec + ";"
+                             + Arrays.toString(transformMatrix));
         }
         try {
             connection.getRemote().focusSearch(accessibilityNodeId, direction,
                     partialInteractiveRegion, interactionId, callback, mFetchFlags,
-                    interrogatingPid, interrogatingTid, spec);
+                    interrogatingPid, interrogatingTid, spec, transformMatrix);
             return mSecurityPolicy.computeValidReportedPackages(
                     connection.getPackageName(), connection.getUid());
         } catch (RemoteException re) {
@@ -1652,6 +1672,23 @@
         mInvocationHandler.startInputLocked(startInputToken, inputContext, editorInfo, restarting);
     }
 
+
+
+    @Nullable
+    Pair<float[], MagnificationSpec> getTransformMatrixAndSpecLocked(int resolvedWindowId) {
+        final WindowInfo windowInfo =
+                mA11yWindowManager.findWindowInfoByIdLocked(resolvedWindowId);
+        if (windowInfo == null) {
+            Slog.w(LOG_TAG, "getTransformMatrixAndSpec, windowInfo is null window id = "
+                    + resolvedWindowId);
+            return new Pair<>(null, null);
+        }
+
+        final MagnificationSpec spec = new MagnificationSpec();
+        spec.setTo(windowInfo.mMagnificationSpec);
+        return new Pair<>(windowInfo.mTransformMatrix, spec);
+    }
+
     /**
      * Called by the invocation handler to notify the service that the
      * state of magnification has changed.
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index e3226c7..ac0c051 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -106,6 +106,7 @@
 import android.view.KeyEvent;
 import android.view.MagnificationSpec;
 import android.view.MotionEvent;
+import android.view.WindowInfo;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityInteractionClient;
@@ -3045,22 +3046,6 @@
         userState.setInteractiveUiTimeoutLocked(newInteractiveUiTimeout);
     }
 
-    @GuardedBy("mLock")
-    @Override
-    public MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId) {
-        IBinder windowToken = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(
-                mCurrentUserId, windowId);
-        if (windowToken != null) {
-            if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) {
-                mTraceManager.logTrace(LOG_TAG + ".getCompatibleMagnificationSpecForWindow",
-                        FLAGS_WINDOW_MANAGER_INTERNAL, "windowToken=" + windowToken);
-            }
-
-            return mWindowManagerService.getCompatibleMagnificationSpecForWindow(windowToken);
-        }
-        return null;
-    }
-
     @Override
     public KeyEventDispatcher getKeyEventDispatcher() {
         if (mKeyEventDispatcher == null) {
@@ -3741,7 +3726,14 @@
                         boundsInScreenBeforeMagnification.centerY());
 
                 // Invert magnification if needed.
-                MagnificationSpec spec = getCompatibleMagnificationSpecLocked(focus.getWindowId());
+                final WindowInfo windowInfo = mA11yWindowManager.findWindowInfoByIdLocked(
+                        focus.getWindowId());
+                MagnificationSpec spec = null;
+                if (windowInfo != null) {
+                    spec = new MagnificationSpec();
+                    spec.setTo(windowInfo.mMagnificationSpec);
+                }
+
                 if (spec != null && !spec.isNop()) {
                     boundsInScreenBeforeMagnification.offset((int) -spec.offsetX,
                             (int) -spec.offsetY);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index aba32ec..e30639c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -52,6 +52,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -478,6 +479,9 @@
             if (oldWindow.taskId != newWindow.taskId) {
                 return true;
             }
+            if (!Arrays.equals(oldWindow.mTransformMatrix, newWindow.mTransformMatrix)) {
+                return true;
+            }
             return false;
         }
 
@@ -800,14 +804,24 @@
                         pw.append(',');
                         pw.println();
                     }
-                    pw.append("Window[");
+                    pw.append("A11yWindow[");
                     AccessibilityWindowInfo window = mWindows.get(j);
                     pw.append(window.toString());
                     pw.append(']');
+                    pw.println();
+                    final WindowInfo windowInfo = findWindowInfoByIdLocked(window.getId());
+                    if (windowInfo != null) {
+                        pw.append("WindowInfo[");
+                        pw.append(windowInfo.toString());
+                        pw.append("]");
+                        pw.println();
+                    }
+
                 }
                 pw.println();
             }
         }
+
     }
     /**
      * Interface to send {@link AccessibilityEvent}.
diff --git a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
index 6828dd9..6958b66 100644
--- a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
+++ b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java
@@ -83,7 +83,7 @@
             mConnectionWithReplacementActions.findAccessibilityNodeInfoByAccessibilityId(
                     AccessibilityNodeInfo.ROOT_NODE_ID, null,
                     mNodeWithReplacementActionsInteractionId, this, 0,
-                    interrogatingPid, interrogatingTid, null, null);
+                    interrogatingPid, interrogatingTid, null, null, null);
         } catch (RemoteException re) {
             if (DEBUG) {
                 Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()");
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index f944d4f..e529010 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -75,6 +75,13 @@
     @VisibleForTesting static final long CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS = 300;
 
     /**
+     * Min time in milliseconds to complete the emergency gesture for it count. If the gesture is
+     * completed faster than this, we assume it's not performed by human and the
+     * event gets ignored.
+     */
+    @VisibleForTesting static final int EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS = 160;
+
+    /**
      * Interval in milliseconds in which the power button must be depressed in succession to be
      * considered part of an extended sequence of taps. Note that this is a looser threshold than
      * the camera launch gesture, because the purpose of this threshold is to measure the
@@ -184,6 +191,7 @@
     private int mEmergencyGesturePowerButtonCooldownPeriodMs;
 
     private long mLastPowerDown;
+    private long mFirstPowerDown;
     private long mLastEmergencyGestureTriggered;
     private int mPowerButtonConsecutiveTaps;
     private int mPowerButtonSlowConsecutiveTaps;
@@ -553,10 +561,12 @@
             mLastPowerDown = event.getEventTime();
             if (powerTapInterval >= POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS) {
                 // Tap too slow, reset consecutive tap counts.
+                mFirstPowerDown  = event.getEventTime();
                 mPowerButtonConsecutiveTaps = 1;
                 mPowerButtonSlowConsecutiveTaps = 1;
             } else if (powerTapInterval >= CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) {
                 // Tap too slow for shortcuts
+                mFirstPowerDown  = event.getEventTime();
                 mPowerButtonConsecutiveTaps = 1;
                 mPowerButtonSlowConsecutiveTaps++;
             } else {
@@ -575,7 +585,26 @@
                     intercept = interactive;
                 }
                 if (mPowerButtonConsecutiveTaps == EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD) {
-                    launchEmergencyGesture = true;
+                    long emergencyGestureSpentTime = event.getEventTime() - mFirstPowerDown;
+                    long emergencyGestureTapDetectionMinTimeMs = Settings.Global.getInt(
+                            mContext.getContentResolver(),
+                            Settings.Global.EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS,
+                            EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS);
+                    if (emergencyGestureSpentTime <= emergencyGestureTapDetectionMinTimeMs) {
+                        Slog.i(TAG, "Emergency gesture detected but it's too fast. Gesture time: "
+                                + emergencyGestureSpentTime + " ms");
+                        // Reset consecutive tap counts.
+                        mFirstPowerDown = event.getEventTime();
+                        mPowerButtonConsecutiveTaps = 1;
+                        mPowerButtonSlowConsecutiveTaps = 1;
+                    } else {
+                        Slog.i(TAG, "Emergency gesture detected. Gesture time: "
+                                + emergencyGestureSpentTime + " ms");
+                        launchEmergencyGesture = true;
+                        mMetricsLogger.histogram("emergency_gesture_spent_time",
+                                (int) emergencyGestureSpentTime);
+
+                    }
                 }
             }
             if (mCameraDoubleTapPowerEnabled
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 2d328d8..210532a 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -153,7 +153,7 @@
 public class VcnManagementService extends IVcnManagementService.Stub {
     @NonNull private static final String TAG = VcnManagementService.class.getSimpleName();
     private static final long DUMP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(5);
-    private static final int LOCAL_LOG_LINE_COUNT = 128;
+    private static final int LOCAL_LOG_LINE_COUNT = 512;
 
     // Public for use in all other VCN classes
     @NonNull public static final LocalLog LOCAL_LOG = new LocalLog(LOCAL_LOG_LINE_COUNT);
@@ -456,7 +456,7 @@
             synchronized (mLock) {
                 final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot;
                 mLastSnapshot = snapshot;
-                logDbg("new snapshot: " + mLastSnapshot);
+                logInfo("new snapshot: " + mLastSnapshot);
 
                 // Start any VCN instances as necessary
                 for (Entry<ParcelUuid, VcnConfig> entry : mConfigs.entrySet()) {
@@ -522,6 +522,8 @@
 
     @GuardedBy("mLock")
     private void stopVcnLocked(@NonNull ParcelUuid uuidToTeardown) {
+        logInfo("Stopping VCN config for subGrp: " + uuidToTeardown);
+
         // Remove in 2 steps. Make sure teardownAsync is triggered before removing from the map.
         final Vcn vcnToTeardown = mVcns.get(uuidToTeardown);
         if (vcnToTeardown == null) {
@@ -567,7 +569,7 @@
 
     @GuardedBy("mLock")
     private void startVcnLocked(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) {
-        logDbg("Starting VCN config for subGrp: " + subscriptionGroup);
+        logInfo("Starting VCN config for subGrp: " + subscriptionGroup);
 
         // TODO(b/193687515): Support multiple VCNs active at the same time
         if (!mVcns.isEmpty()) {
@@ -626,7 +628,7 @@
         if (!config.getProvisioningPackageName().equals(opPkgName)) {
             throw new IllegalArgumentException("Mismatched caller and VcnConfig creator");
         }
-        logDbg("VCN config updated for subGrp: " + subscriptionGroup);
+        logInfo("VCN config updated for subGrp: " + subscriptionGroup);
 
         mContext.getSystemService(AppOpsManager.class)
                 .checkPackage(mDeps.getBinderCallingUid(), config.getProvisioningPackageName());
@@ -652,7 +654,7 @@
     public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull String opPkgName) {
         requireNonNull(subscriptionGroup, "subscriptionGroup was null");
         requireNonNull(opPkgName, "opPkgName was null");
-        logDbg("VCN config cleared for subGrp: " + subscriptionGroup);
+        logInfo("VCN config cleared for subGrp: " + subscriptionGroup);
 
         mContext.getSystemService(AppOpsManager.class)
                 .checkPackage(mDeps.getBinderCallingUid(), opPkgName);
@@ -1050,24 +1052,34 @@
         Slog.d(TAG, msg, tr);
     }
 
+    private void logInfo(String msg) {
+        Slog.i(TAG, msg);
+        LOCAL_LOG.log("[INFO] [" + TAG + "] " + msg);
+    }
+
+    private void logInfo(String msg, Throwable tr) {
+        Slog.i(TAG, msg, tr);
+        LOCAL_LOG.log("[INFO] [" + TAG + "] " + msg + tr);
+    }
+
     private void logErr(String msg) {
         Slog.e(TAG, msg);
-        LOCAL_LOG.log(TAG + " ERR: " + msg);
+        LOCAL_LOG.log("[ERR] [" + TAG + "] " + msg);
     }
 
     private void logErr(String msg, Throwable tr) {
         Slog.e(TAG, msg, tr);
-        LOCAL_LOG.log(TAG + " ERR: " + msg + tr);
+        LOCAL_LOG.log("[ERR ] [" + TAG + "] " + msg + tr);
     }
 
     private void logWtf(String msg) {
         Slog.wtf(TAG, msg);
-        LOCAL_LOG.log(TAG + " WTF: " + msg);
+        LOCAL_LOG.log("[WTF] [" + TAG + "] " + msg);
     }
 
     private void logWtf(String msg, Throwable tr) {
         Slog.wtf(TAG, msg, tr);
-        LOCAL_LOG.log(TAG + " WTF: " + msg + tr);
+        LOCAL_LOG.log("[WTF ] [" + TAG + "] " + msg + tr);
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index efde2a5..35f7e06 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -49,6 +49,12 @@
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED;
+import static android.net.ConnectivityManager.BLOCKED_REASON_APP_STANDBY;
+import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER;
+import static android.net.ConnectivityManager.BLOCKED_REASON_DOZE;
+import static android.net.ConnectivityManager.BLOCKED_REASON_LOW_POWER_STANDBY;
 import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
 import static android.os.FactoryTest.FACTORY_TEST_OFF;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
@@ -4815,7 +4821,7 @@
             }
 
             checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
-            bindApplicationTimeMillis = SystemClock.elapsedRealtime();
+            bindApplicationTimeMillis = SystemClock.uptimeMillis();
             mAtmInternal.preBindApplication(app.getWindowProcessController());
             final ActiveInstrumentation instr2 = app.getActiveInstrumentation();
             if (mPlatformCompat != null) {
@@ -4963,9 +4969,9 @@
                 pid,
                 app.info.packageName,
                 FrameworkStatsLog.PROCESS_START_TIME__TYPE__COLD,
-                app.getStartTime(),
-                (int) (bindApplicationTimeMillis - app.getStartTime()),
-                (int) (SystemClock.elapsedRealtime() - app.getStartTime()),
+                app.getStartElapsedTime(),
+                (int) (bindApplicationTimeMillis - app.getStartUptime()),
+                (int) (SystemClock.uptimeMillis() - app.getStartUptime()),
                 app.getHostingRecord().getType(),
                 (app.getHostingRecord().getName() != null ? app.getHostingRecord().getName() : ""));
         return true;
@@ -17306,8 +17312,22 @@
                 // TODO: We can reuse this data in
                 // ProcessList#incrementProcStateSeqAndNotifyAppsLOSP instead of calling into
                 // NetworkManagementService.
-                return mUidNetworkBlockedReasons.get(uid, BLOCKED_REASON_NONE)
-                        != BLOCKED_REASON_NONE;
+                final int uidBlockedReasons = mUidNetworkBlockedReasons.get(
+                        uid, BLOCKED_REASON_NONE);
+                if (uidBlockedReasons == BLOCKED_REASON_NONE) {
+                    return false;
+                }
+                final int topExemptedBlockedReasons = BLOCKED_REASON_BATTERY_SAVER
+                        | BLOCKED_REASON_DOZE
+                        | BLOCKED_REASON_APP_STANDBY
+                        | BLOCKED_REASON_LOW_POWER_STANDBY
+                        | BLOCKED_METERED_REASON_DATA_SAVER
+                        | BLOCKED_METERED_REASON_USER_RESTRICTED;
+                final int effectiveBlockedReasons =
+                        uidBlockedReasons & ~topExemptedBlockedReasons;
+                // Only consider it as blocked if it is not blocked by a reason
+                // that is not exempted by app being in the top state.
+                return effectiveBlockedReasons == BLOCKED_REASON_NONE;
             }
         }
 
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 64ff532..90201a0 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -654,7 +654,7 @@
             final long start = stats.getStatsStartTimestamp();
             final long end = stats.getStatsEndTimestamp();
             final double scale = expectedDuration > 0
-                    ? (expectedDuration * 1.0d) / (end - start) : 1.0d;
+                    ? Math.min((expectedDuration * 1.0d) / (end - start), 1.0d) : 1.0d;
             final AppBatteryPolicy bgPolicy = mInjector.getPolicy();
             for (UidBatteryConsumer uidConsumer : uidConsumers) {
                 // TODO: b/200326767 - as we are not supporting per proc state attribution yet,
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index 798647e..635d86c 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -223,6 +223,11 @@
     private static final String ATTR_LEVEL_TS = "levelts";
     private static final String ATTR_REASON = "reason";
 
+    private static final String[] ROLES_IN_INTEREST = {
+        RoleManager.ROLE_DIALER,
+        RoleManager.ROLE_EMERGENCY,
+    };
+
     private final Context mContext;
     private final HandlerThread mBgHandlerThread;
     private final BgHandler mBgHandler;
@@ -1386,6 +1391,7 @@
         initBgRestrictionExemptioFromSysConfig();
         initRestrictionStates();
         initSystemModuleNames();
+        initRolesInInterest();
         registerForUidObservers();
         registerForSystemBroadcasts();
         mNotificationHelper.onSystemReady();
@@ -2666,6 +2672,18 @@
         }
     }
 
+    private void initRolesInInterest() {
+        final int[] allUsers = mInjector.getUserManagerInternal().getUserIds();
+        for (String role : ROLES_IN_INTEREST) {
+            if (mInjector.getRoleManager().isRoleAvailable(role)) {
+                for (int userId : allUsers) {
+                    final UserHandle user = UserHandle.of(userId);
+                    onRoleHoldersChanged(role, user);
+                }
+            }
+        }
+    }
+
     private void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) {
         final List<String> rolePkgs = mInjector.getRoleManager().getRoleHoldersAsUser(
                 roleName, user);
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 0518899..8a7fece 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -61,7 +61,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.PowerExemptionManager;
 import android.os.PowerExemptionManager.ReasonCode;
 import android.os.PowerExemptionManager.TempAllowListType;
 import android.os.Process;
@@ -1934,9 +1933,6 @@
     }
 
     private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r, int targetUid) {
-        // STOPSHIP (217251579): Temporarily use temp-allowlist reason to identify
-        // push messages and record response events.
-        useTemporaryAllowlistReasonAsSignal(r);
         if (r.options == null || r.options.getIdForResponseEvent() <= 0) {
             return;
         }
@@ -1951,17 +1947,6 @@
                 mService.getUidStateLocked(targetUid));
     }
 
-    private void useTemporaryAllowlistReasonAsSignal(BroadcastRecord r) {
-        if (r.options == null || r.options.getIdForResponseEvent() > 0) {
-            return;
-        }
-        final int reasonCode = r.options.getTemporaryAppAllowlistReasonCode();
-        if (reasonCode == PowerExemptionManager.REASON_PUSH_MESSAGING
-                || reasonCode == PowerExemptionManager.REASON_PUSH_MESSAGING_OVER_QUOTA) {
-            r.options.recordResponseEventWhileInBackground(reasonCode);
-        }
-    }
-
     @NonNull
     private UsageStatsManagerInternal getUsageStatsManagerInternal() {
         final UsageStatsManagerInternal usageStatsManagerInternal =
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index b886196..c043773 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -173,7 +173,7 @@
     // Message constant to clear {@link UserJourneySession} from {@link mUserIdToUserJourneyMap} if
     // the user journey, defined in the UserLifecycleJourneyReported atom for statsd, is not
     // complete within {@link USER_JOURNEY_TIMEOUT}.
-    private static final int CLEAR_USER_JOURNEY_SESSION_MSG = 200;
+    static final int CLEAR_USER_JOURNEY_SESSION_MSG = 200;
     // Wait time for completing the user journey. If a user journey is not complete within this
     // time, the remaining lifecycle events for the journey would not be logged in statsd.
     // Timeout set for 90 seconds.
@@ -209,12 +209,15 @@
             FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_START;
     private static final int USER_JOURNEY_USER_CREATE =
             FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE;
+    private static final int USER_JOURNEY_USER_STOP =
+            FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_STOP;
     @IntDef(prefix = { "USER_JOURNEY" }, value = {
             USER_JOURNEY_UNKNOWN,
             USER_JOURNEY_USER_SWITCH_FG,
             USER_JOURNEY_USER_SWITCH_UI,
             USER_JOURNEY_USER_START,
             USER_JOURNEY_USER_CREATE,
+            USER_JOURNEY_USER_STOP
     })
     @interface UserJourney {}
 
@@ -233,6 +236,8 @@
             FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNLOCKING_USER;
     private static final int USER_LIFECYCLE_EVENT_UNLOCKED_USER =
             FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNLOCKED_USER;
+    private static final int USER_LIFECYCLE_EVENT_STOP_USER =
+            FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__STOP_USER;
     @IntDef(prefix = { "USER_LIFECYCLE_EVENT" }, value = {
             USER_LIFECYCLE_EVENT_UNKNOWN,
             USER_LIFECYCLE_EVENT_SWITCH_USER,
@@ -241,6 +246,7 @@
             USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED,
             USER_LIFECYCLE_EVENT_UNLOCKING_USER,
             USER_LIFECYCLE_EVENT_UNLOCKED_USER,
+            USER_LIFECYCLE_EVENT_STOP_USER
     })
     @interface UserLifecycleEvent {}
 
@@ -1008,6 +1014,10 @@
             return;
         }
 
+        logUserJourneyInfo(null, getUserInfo(userId), USER_JOURNEY_USER_STOP);
+        logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER,
+                USER_LIFECYCLE_EVENT_STATE_BEGIN);
+
         if (stopUserCallback != null) {
             uss.mStopCallbacks.add(stopUserCallback);
         }
@@ -1066,6 +1076,9 @@
         synchronized (mLock) {
             if (uss.state != UserState.STATE_STOPPING) {
                 // Whoops, we are being started back up.  Abort, abort!
+                logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER,
+                        USER_LIFECYCLE_EVENT_STATE_NONE);
+                clearSessionId(userId);
                 return;
             }
             uss.setState(UserState.STATE_SHUTDOWN);
@@ -1165,10 +1178,18 @@
                 mInjector.getUserManager().removeUserEvenWhenDisallowed(userId);
             }
 
+            logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER,
+                    USER_LIFECYCLE_EVENT_STATE_FINISH);
+            clearSessionId(userId);
+
             if (!lockUser) {
                 return;
             }
             dispatchUserLocking(userIdToLock, keyEvictedCallbacks);
+        } else {
+            logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER,
+                    USER_LIFECYCLE_EVENT_STATE_NONE);
+            clearSessionId(userId);
         }
     }
 
@@ -2962,13 +2983,13 @@
             if (userJourneySession != null) {
                 // TODO(b/157007231): Move this logic to a separate class/file.
                 if ((userJourneySession.mJourney == USER_JOURNEY_USER_SWITCH_UI
-                        && journey == USER_JOURNEY_USER_START)
-                        || (userJourneySession.mJourney == USER_JOURNEY_USER_SWITCH_FG
-                                && journey == USER_JOURNEY_USER_START)) {
+                        || userJourneySession.mJourney == USER_JOURNEY_USER_SWITCH_FG)
+                        && (journey == USER_JOURNEY_USER_START
+                                || journey == USER_JOURNEY_USER_STOP)) {
                     /*
-                     * There is already a user switch journey, and a user start journey for the same
-                     * target user received. User start journey is most likely a part of user switch
-                     * journey so no need to create a new journey for user start.
+                     * There is already a user switch journey, and a user start or stop journey for
+                     * the same target user received. New journey is most likely a part of user
+                     * switch journey so no need to create a new journey.
                      */
                     if (DEBUG_MU) {
                         Slogf.d(TAG, journey + " not logged as it is expected to be part of "
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 465e5e9..b1b5d3f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -9712,7 +9712,7 @@
     //==========================================================================================
     static final int LOG_NB_EVENTS_LIFECYCLE = 20;
     static final int LOG_NB_EVENTS_PHONE_STATE = 20;
-    static final int LOG_NB_EVENTS_DEVICE_CONNECTION = 30;
+    static final int LOG_NB_EVENTS_DEVICE_CONNECTION = 50;
     static final int LOG_NB_EVENTS_FORCE_USE = 20;
     static final int LOG_NB_EVENTS_VOLUME = 40;
     static final int LOG_NB_EVENTS_DYN_POLICY = 10;
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 7db99f1..be11253 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -239,6 +239,8 @@
         public long mResultErrorCount;
         public boolean mDeviceError;
         public List<CameraStreamStats> mStreamStats;
+        public String mUserTag;
+
         private long mDurationOrStartTimeMs;  // Either start time, or duration once completed
 
         CameraUsageEvent(String cameraId, int facing, String clientName, int apiLevel,
@@ -257,7 +259,7 @@
 
         public void markCompleted(int internalReconfigure, long requestCount,
                 long resultErrorCount, boolean deviceError,
-                List<CameraStreamStats>  streamStats) {
+                List<CameraStreamStats>  streamStats, String userTag) {
             if (mCompleted) {
                 return;
             }
@@ -268,6 +270,7 @@
             mResultErrorCount = resultErrorCount;
             mDeviceError = deviceError;
             mStreamStats = streamStats;
+            mUserTag = userTag;
             if (CameraServiceProxy.DEBUG) {
                 Slog.v(TAG, "A camera facing " + cameraFacingToString(mCameraFacing) +
                         " was in use by " + mClientName + " for " +
@@ -794,7 +797,8 @@
                         + ", requestCount " + e.mRequestCount
                         + ", resultErrorCount " + e.mResultErrorCount
                         + ", deviceError " + e.mDeviceError
-                        + ", streamCount is " + streamCount);
+                        + ", streamCount is " + streamCount
+                        + ", userTag is " + e.mUserTag);
             }
             // Convert from CameraStreamStats to CameraStreamProto
             CameraStreamProto[] streamProtos = new CameraStreamProto[MAX_STREAM_STATISTICS];
@@ -851,7 +855,8 @@
                     MessageNano.toByteArray(streamProtos[1]),
                     MessageNano.toByteArray(streamProtos[2]),
                     MessageNano.toByteArray(streamProtos[3]),
-                    MessageNano.toByteArray(streamProtos[4]));
+                    MessageNano.toByteArray(streamProtos[4]),
+                    e.mUserTag);
         }
     }
 
@@ -1038,6 +1043,7 @@
         long resultErrorCount = cameraState.getResultErrorCount();
         boolean deviceError = cameraState.getDeviceErrorFlag();
         List<CameraStreamStats> streamStats = cameraState.getStreamStats();
+        String userTag = cameraState.getUserTag();
         synchronized(mLock) {
             // Update active camera list and notify NFC if necessary
             boolean wasEmpty = mActiveCameraUsage.isEmpty();
@@ -1091,7 +1097,8 @@
                     if (oldEvent != null) {
                         Slog.w(TAG, "Camera " + cameraId + " was already marked as active");
                         oldEvent.markCompleted(/*internalReconfigure*/0, /*requestCount*/0,
-                                /*resultErrorCount*/0, /*deviceError*/false, streamStats);
+                                /*resultErrorCount*/0, /*deviceError*/false, streamStats,
+                                /*userTag*/"");
                         mCameraUsageHistory.add(oldEvent);
                     }
                     break;
@@ -1101,7 +1108,7 @@
                     if (doneEvent != null) {
 
                         doneEvent.markCompleted(internalReconfigureCount, requestCount,
-                                resultErrorCount, deviceError, streamStats);
+                                resultErrorCount, deviceError, streamStats, userTag);
                         mCameraUsageHistory.add(doneEvent);
 
                         // Check current active camera IDs to see if this package is still
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 4c9b28b..d9e4828 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -20,6 +20,7 @@
 import static android.Manifest.permission.CONTROL_VPN;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.RouteInfo.RTN_THROW;
 import static android.net.RouteInfo.RTN_UNREACHABLE;
 import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN;
@@ -2549,6 +2550,7 @@
                 req = new NetworkRequest.Builder()
                         .clearCapabilities()
                         .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+                        .addCapability(NET_CAPABILITY_NOT_VPN)
                         .build();
             } else {
                 // Basically, the request here is referring to the default request which is defined
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index a155095..982ac3c 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -76,6 +76,8 @@
 
     private final boolean mIsBootDisplayModeSupported;
 
+    private Context mOverlayContext;
+
     // Called with SyncRoot lock held.
     public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
             Context context, Handler handler, Listener listener) {
@@ -1222,7 +1224,10 @@
 
     /** Supplies a context whose Resources apply runtime-overlays */
     Context getOverlayContext() {
-        return ActivityThread.currentActivityThread().getSystemUiContext();
+        if (mOverlayContext == null) {
+            mOverlayContext = ActivityThread.currentActivityThread().getSystemUiContext();
+        }
+        return mOverlayContext;
     }
 
     /**
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
index 1c296e5..8647680 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
@@ -83,7 +83,9 @@
     private final HdmiCecLocalDevice mDevice;
 
     private final SparseArray<CecMessageHandler> mCecMessageHandlers = new SparseArray<>();
-    private final CecMessageHandler mDefaultHandler = new Aborter(
+    private final CecMessageHandler mDefaultHandler;
+
+    private final CecMessageHandler mAborterUnrecognizedOpcode = new Aborter(
             Constants.ABORT_UNRECOGNIZED_OPCODE);
     private final CecMessageHandler mAborterIncorrectMode = new Aborter(
             Constants.ABORT_NOT_IN_CORRECT_MODE);
@@ -95,6 +97,10 @@
             mUserControlProcessedHandler = new UserControlProcessedHandler();
 
     private void addCommonHandlers() {
+        addHandler(Constants.MESSAGE_USER_CONTROL_PRESSED, mUserControlProcessedHandler);
+    }
+
+    private void addTvHandlers() {
         addHandler(Constants.MESSAGE_ACTIVE_SOURCE, mBystander);
         addHandler(Constants.MESSAGE_REQUEST_ACTIVE_SOURCE, mBystander);
         addHandler(Constants.MESSAGE_ROUTING_CHANGE, mBystander);
@@ -118,17 +124,13 @@
         addHandler(Constants.MESSAGE_REPORT_POWER_STATUS, mBypasser);
         addHandler(Constants.MESSAGE_GIVE_FEATURES, mBypasser);
 
-        addHandler(Constants.MESSAGE_USER_CONTROL_PRESSED, mUserControlProcessedHandler);
-
         addHandler(Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS, mBypasser);
         addHandler(Constants.MESSAGE_ABORT, mBypasser);
         addHandler(Constants.MESSAGE_GET_CEC_VERSION, mBypasser);
 
         addHandler(Constants.MESSAGE_VENDOR_COMMAND_WITH_ID, mAborterIncorrectMode);
         addHandler(Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE, mAborterIncorrectMode);
-    }
 
-    private void addTvHandlers() {
         addHandler(Constants.MESSAGE_IMAGE_VIEW_ON, mAutoOnHandler);
         addHandler(Constants.MESSAGE_TEXT_VIEW_ON, mAutoOnHandler);
 
@@ -153,6 +155,9 @@
         addCommonHandlers();
         if (mDevice.getType() == HdmiDeviceInfo.DEVICE_TV) {
             addTvHandlers();
+            mDefaultHandler = mAborterUnrecognizedOpcode;
+        } else {
+            mDefaultHandler = mBypasser;
         }
     }
 
diff --git a/services/core/java/com/android/server/health/HealthRegCallbackAidl.java b/services/core/java/com/android/server/health/HealthRegCallbackAidl.java
index 629011a..90a2f48 100644
--- a/services/core/java/com/android/server/health/HealthRegCallbackAidl.java
+++ b/services/core/java/com/android/server/health/HealthRegCallbackAidl.java
@@ -115,5 +115,13 @@
         public void healthInfoChanged(HealthInfo healthInfo) throws RemoteException {
             mServiceInfoCallback.update(healthInfo);
         }
+        @Override
+        public String getInterfaceHash() {
+            return IHealthInfoCallback.HASH;
+        }
+        @Override
+        public int getInterfaceVersion() {
+            return IHealthInfoCallback.VERSION;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index a1ee46b..acc0746 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -389,6 +389,15 @@
                     mCallback.handleTransactionResult(transactionId, success);
                 });
             }
+            @Override
+            public String getInterfaceHash() {
+                return android.hardware.contexthub.IContextHubCallback.HASH;
+            }
+
+            @Override
+            public int getInterfaceVersion() {
+                return android.hardware.contexthub.IContextHubCallback.VERSION;
+            }
         }
 
         ContextHubWrapperAidl(android.hardware.contexthub.IContextHub hub) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 0f9945c..69385a9 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -319,7 +319,7 @@
         }
 
         if (mGnssAntennaInfoProvider.isSupported()) {
-            ipw.println("Navigation Message Provider:");
+            ipw.println("Antenna Info Provider:");
             ipw.increaseIndent();
             ipw.println("Antenna Infos: " + mGnssAntennaInfoProvider.getAntennaInfos());
             mGnssAntennaInfoProvider.dump(fd, ipw, args);
diff --git a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
index b45bfb1..79088d0 100644
--- a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
+++ b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
@@ -116,6 +116,10 @@
         }
 
         mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+        if (mPackageName == null || mPackageName.length() == 0) {
+            throw new NullPointerException("Package Name is null");
+        }
+
         mUid = intent.getIntExtra("com.android.server.logcat.uid", 0);
         mGid = intent.getIntExtra("com.android.server.logcat.gid", 0);
         mPid = intent.getIntExtra("com.android.server.logcat.pid", 0);
@@ -154,12 +158,17 @@
         CharSequence appLabel = pm.getApplicationInfoAsUser(callingPackage,
                 PackageManager.MATCH_DIRECT_BOOT_AUTO,
                 UserHandle.getUserId(uid)).loadLabel(pm);
-        if (appLabel == null) {
+        if (appLabel == null || appLabel.length() == 0) {
             throw new NameNotFoundException("Application Label is null");
         }
 
-        return context.getString(com.android.internal.R.string.log_access_confirmation_title,
-            appLabel);
+        String titleString = context.getString(
+                com.android.internal.R.string.log_access_confirmation_title, appLabel);
+        if (titleString == null || titleString.length() == 0) {
+            throw new NullPointerException("Title is null");
+        }
+
+        return titleString;
     }
 
     /**
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f42e734..8ed145c 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -240,6 +240,7 @@
 import android.service.notification.StatusBarNotification;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeProto;
+import android.telecom.TelecomManager;
 import android.telephony.PhoneStateListener;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -509,6 +510,7 @@
     private ShortcutHelper mShortcutHelper;
     private PermissionHelper mPermissionHelper;
     private UsageStatsManagerInternal mUsageStatsManagerInternal;
+    private TelecomManager mTelecomManager;
 
     final IBinder mForegroundToken = new Binder();
     private WorkerHandler mHandler;
@@ -2100,7 +2102,8 @@
             NotificationHistoryManager historyManager, StatsManager statsManager,
             TelephonyManager telephonyManager, ActivityManagerInternal ami,
             MultiRateLimiter toastRateLimiter, PermissionHelper permissionHelper,
-            UsageStatsManagerInternal usageStatsManagerInternal) {
+            UsageStatsManagerInternal usageStatsManagerInternal,
+            TelecomManager telecomManager) {
         mHandler = handler;
         Resources resources = getContext().getResources();
         mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
@@ -2129,6 +2132,7 @@
         mDeviceIdleManager = getContext().getSystemService(DeviceIdleManager.class);
         mDpm = dpm;
         mUm = userManager;
+        mTelecomManager = telecomManager;
         mPlatformCompat = IPlatformCompat.Stub.asInterface(
                 ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
 
@@ -2420,7 +2424,8 @@
                         PermissionManagerServiceInternal.class), AppGlobals.getPackageManager(),
                         AppGlobals.getPermissionManager(), mEnableAppSettingMigration,
                         mForceUserSetOnUpgrade),
-                LocalServices.getService(UsageStatsManagerInternal.class));
+                LocalServices.getService(UsageStatsManagerInternal.class),
+                getContext().getSystemService(TelecomManager.class));
 
         publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,
                 DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);
@@ -5535,6 +5540,12 @@
         }
 
         @Override
+        public boolean isInCall(String pkg, int uid) {
+            checkCallerIsSystemOrSystemUiOrShell();
+            return isCallNotification(pkg, uid);
+        }
+
+        @Override
         public void setPrivateNotificationsAllowed(boolean allow) {
             if (PackageManager.PERMISSION_GRANTED
                     != getContext().checkCallingPermission(
@@ -6846,7 +6857,7 @@
         synchronized (mNotificationLock) {
             isBlocked |= isRecordBlockedLocked(r);
         }
-        if (isBlocked && !n.isMediaNotification()) {
+        if (isBlocked && !(n.isMediaNotification() || isCallNotification(pkg, uid, n))) {
             if (DBG) {
                 Slog.e(TAG, "Suppressing notification from package " + r.getSbn().getPackageName()
                         + " by user request.");
@@ -6858,6 +6869,23 @@
         return true;
     }
 
+    private boolean isCallNotification(String pkg, int uid, Notification n) {
+        if (n.isStyle(Notification.CallStyle.class)) {
+            return isCallNotification(pkg, uid);
+        }
+        return false;
+    }
+
+    private boolean isCallNotification(String pkg, int uid) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mTelecomManager.isInManagedCall() || mTelecomManager.isInSelfManagedCall(
+                    pkg, UserHandle.getUserHandleForUid(uid));
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     private boolean areNotificationsEnabledForPackageInt(String pkg, int uid) {
         if (mEnableAppSettingMigration) {
             return mPermissionHelper.hasPermission(uid);
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index b4230c1..a09aa7c 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -55,8 +55,10 @@
     private final PermissionManagerServiceInternal mPmi;
     private final IPackageManager mPackageManager;
     private final IPermissionManager mPermManager;
-    // TODO (b/194833441): Remove when the migration is enabled
+    // TODO (b/194833441): Remove this boolean (but keep the isMigrationEnabled() method)
+    //  when the migration is enabled
     private final boolean mMigrationEnabled;
+    private final boolean mIsTv;
     private final boolean mForceUserSetOnUpgrade;
 
     public PermissionHelper(PermissionManagerServiceInternal pmi, IPackageManager packageManager,
@@ -67,10 +69,17 @@
         mPermManager = permManager;
         mMigrationEnabled = migrationEnabled;
         mForceUserSetOnUpgrade = forceUserSetOnUpgrade;
+        boolean isTv;
+        try {
+            isTv = mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK, 0);
+        } catch (RemoteException e) {
+            isTv = false;
+        }
+        mIsTv = isTv;
     }
 
     public boolean isMigrationEnabled() {
-        return mMigrationEnabled;
+        return mMigrationEnabled && !mIsTv;
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java
index 5865adb..dff7100 100644
--- a/services/core/java/com/android/server/pm/AppsFilterImpl.java
+++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java
@@ -76,6 +76,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -102,6 +103,7 @@
      * application B is implicitly allowed to query for application A; regardless of any manifest
      * entries.
      */
+    @GuardedBy("mLock")
     @Watched
     private final WatchedSparseSetArray<Integer> mImplicitlyQueryable;
     private final SnapshotCache<WatchedSparseSetArray<Integer>> mImplicitQueryableSnapshot;
@@ -111,6 +113,7 @@
      * interacted with it, but could keep across package updates. For example, if application A
      * grants persistable uri permission to application B; regardless of any manifest entries.
      */
+    @GuardedBy("mLock")
     @Watched
     private final WatchedSparseSetArray<Integer> mRetainedImplicitlyQueryable;
     private final SnapshotCache<WatchedSparseSetArray<Integer>>
@@ -120,6 +123,7 @@
      * A mapping from the set of App IDs that query other App IDs via package name to the
      * list of packages that they can see.
      */
+    @GuardedBy("mLock")
     @Watched
     private final WatchedSparseSetArray<Integer> mQueriesViaPackage;
     private final SnapshotCache<WatchedSparseSetArray<Integer>> mQueriesViaPackageSnapshot;
@@ -128,6 +132,7 @@
      * A mapping from the set of App IDs that query others via component match to the list
      * of packages that the they resolve to.
      */
+    @GuardedBy("mLock")
     @Watched
     private final WatchedSparseSetArray<Integer> mQueriesViaComponent;
     private final SnapshotCache<WatchedSparseSetArray<Integer>> mQueriesViaComponentSnapshot;
@@ -136,6 +141,7 @@
      * A mapping from the set of App IDs that query other App IDs via library name to the
      * list of packages that they can see.
      */
+    @GuardedBy("mLock")
     @Watched
     private final WatchedSparseSetArray<Integer> mQueryableViaUsesLibrary;
     private final SnapshotCache<WatchedSparseSetArray<Integer>> mQueryableViaUsesLibrarySnapshot;
@@ -158,6 +164,7 @@
      * A set of App IDs that are always queryable by any package, regardless of their manifest
      * content.
      */
+    @GuardedBy("mLock")
     @Watched
     private final WatchedArraySet<Integer> mForceQueryable;
     private final SnapshotCache<WatchedArraySet<Integer>> mForceQueryableSnapshot;
@@ -173,9 +180,9 @@
     private final FeatureConfig mFeatureConfig;
     private final OverlayReferenceMapper mOverlayReferenceMapper;
     private final StateProvider mStateProvider;
-    private final PackageManagerInternal mPmInternal;
     private SigningDetails mSystemSigningDetails;
 
+    @GuardedBy("mLock")
     @Watched
     private final WatchedArrayList<String> mProtectedBroadcasts;
     private final SnapshotCache<WatchedArrayList<String>> mProtectedBroadcastsSnapshot;
@@ -197,6 +204,11 @@
     private volatile boolean mSystemReady = false;
 
     /**
+     * Guards the accesses for the list/set fields except for {@link #mShouldFilterCache}
+     */
+    private final Object mLock = new Object();
+
+    /**
      * A cached snapshot.
      */
     private final SnapshotCache<AppsFilterImpl> mSnapshot;
@@ -216,15 +228,6 @@
      * Watchable machinery
      */
     private final WatchableImpl mWatchable = new WatchableImpl();
-    /**
-     * The observer that watches for changes from array members
-     */
-    private final Watcher mObserver = new Watcher() {
-        @Override
-        public void onChange(@Nullable Watchable what) {
-            AppsFilterImpl.this.dispatchChange(what);
-        }
-    };
 
     /**
      * Ensures an observer is in the list, exactly once. The observer cannot be null.  The
@@ -284,15 +287,13 @@
             String[] forceQueryableList,
             boolean systemAppsQueryable,
             @Nullable OverlayReferenceMapper.Provider overlayProvider,
-            Executor backgroundExecutor,
-            PackageManagerInternal pmInternal) {
+            Executor backgroundExecutor) {
         mFeatureConfig = featureConfig;
         mForceQueryableByDevicePackageNames = forceQueryableList;
         mSystemAppsQueryable = systemAppsQueryable;
         mOverlayReferenceMapper = new OverlayReferenceMapper(true /*deferRebuild*/,
                 overlayProvider);
         mStateProvider = stateProvider;
-        mPmInternal = pmInternal;
         mBackgroundExecutor = backgroundExecutor;
         mShouldFilterCache = new WatchedSparseBooleanMatrix();
         mShouldFilterCacheSnapshot = new SnapshotCache.Auto<>(
@@ -321,8 +322,6 @@
         mProtectedBroadcastsSnapshot = new SnapshotCache.Auto<>(
                 mProtectedBroadcasts, mProtectedBroadcasts, "AppsFilter.mProtectedBroadcasts");
 
-        registerObservers();
-        Watchable.verifyWatchedAttributes(this, mObserver);
         mSnapshot = makeCache();
     }
 
@@ -330,20 +329,22 @@
      * The copy constructor is used by PackageManagerService to construct a snapshot.
      */
     private AppsFilterImpl(AppsFilterImpl orig) {
-        mImplicitlyQueryable = orig.mImplicitQueryableSnapshot.snapshot();
-        mImplicitQueryableSnapshot = new SnapshotCache.Sealed<>();
-        mRetainedImplicitlyQueryable = orig.mRetainedImplicitlyQueryableSnapshot.snapshot();
-        mRetainedImplicitlyQueryableSnapshot = new SnapshotCache.Sealed<>();
-        mQueriesViaPackage = orig.mQueriesViaPackageSnapshot.snapshot();
-        mQueriesViaPackageSnapshot = new SnapshotCache.Sealed<>();
-        mQueriesViaComponent = orig.mQueriesViaComponentSnapshot.snapshot();
-        mQueriesViaComponentSnapshot = new SnapshotCache.Sealed<>();
-        mQueryableViaUsesLibrary = orig.mQueryableViaUsesLibrarySnapshot.snapshot();
-        mQueryableViaUsesLibrarySnapshot = new SnapshotCache.Sealed<>();
-        mForceQueryable = orig.mForceQueryableSnapshot.snapshot();
-        mForceQueryableSnapshot = new SnapshotCache.Sealed<>();
-        mProtectedBroadcasts = orig.mProtectedBroadcastsSnapshot.snapshot();
-        mProtectedBroadcastsSnapshot = new SnapshotCache.Sealed<>();
+        synchronized (orig.mLock) {
+            mImplicitlyQueryable = orig.mImplicitQueryableSnapshot.snapshot();
+            mImplicitQueryableSnapshot = new SnapshotCache.Sealed<>();
+            mRetainedImplicitlyQueryable = orig.mRetainedImplicitlyQueryableSnapshot.snapshot();
+            mRetainedImplicitlyQueryableSnapshot = new SnapshotCache.Sealed<>();
+            mQueriesViaPackage = orig.mQueriesViaPackageSnapshot.snapshot();
+            mQueriesViaPackageSnapshot = new SnapshotCache.Sealed<>();
+            mQueriesViaComponent = orig.mQueriesViaComponentSnapshot.snapshot();
+            mQueriesViaComponentSnapshot = new SnapshotCache.Sealed<>();
+            mQueryableViaUsesLibrary = orig.mQueryableViaUsesLibrarySnapshot.snapshot();
+            mQueryableViaUsesLibrarySnapshot = new SnapshotCache.Sealed<>();
+            mForceQueryable = orig.mForceQueryableSnapshot.snapshot();
+            mForceQueryableSnapshot = new SnapshotCache.Sealed<>();
+            mProtectedBroadcasts = orig.mProtectedBroadcastsSnapshot.snapshot();
+            mProtectedBroadcastsSnapshot = new SnapshotCache.Sealed<>();
+        }
         mQueriesViaComponentRequireRecompute = orig.mQueriesViaComponentRequireRecompute;
         mForceQueryableByDevicePackageNames =
                 Arrays.copyOf(orig.mForceQueryableByDevicePackageNames,
@@ -359,23 +360,10 @@
         }
 
         mBackgroundExecutor = null;
-        mPmInternal = null;
         mSnapshot = new SnapshotCache.Sealed<>();
         mSystemReady = true;
     }
 
-    @SuppressWarnings("GuardedBy")
-    private void registerObservers() {
-        mImplicitlyQueryable.registerObserver(mObserver);
-        mRetainedImplicitlyQueryable.registerObserver(mObserver);
-        mQueriesViaPackage.registerObserver(mObserver);
-        mQueriesViaComponent.registerObserver(mObserver);
-        mQueryableViaUsesLibrary.registerObserver(mObserver);
-        mForceQueryable.registerObserver(mObserver);
-        mProtectedBroadcasts.registerObserver(mObserver);
-        mShouldFilterCache.registerObserver(mObserver);
-    }
-
     /**
      * Return a snapshot.  If the cached snapshot is null, build a new one.  The logic in
      * the function ensures that this function returns a valid snapshot even if a race
@@ -397,6 +385,7 @@
 
         interface CurrentStateCallback {
             void currentState(ArrayMap<String, ? extends PackageStateInternal> settings,
+                    Collection<SharedUserSetting> sharedUserSettings,
                     UserInfo[] users);
         }
     }
@@ -588,12 +577,13 @@
         final StateProvider stateProvider = command -> {
             synchronized (injector.getLock()) {
                 command.currentState(injector.getSettings().getPackagesLocked().untrackedStorage(),
+                        injector.getSettings().getAllSharedUsersLPw(),
                         injector.getUserManagerInternal().getUserInfos());
             }
         };
         AppsFilterImpl appsFilter = new AppsFilterImpl(stateProvider, featureConfig,
                 forcedQueryablePackageNames, forceSystemAppsQueryable, null,
-                injector.getBackgroundExecutor(), pmInt);
+                injector.getBackgroundExecutor());
         featureConfig.setAppsFilter(appsFilter);
         return appsFilter;
     }
@@ -743,9 +733,11 @@
             return false;
         }
         final boolean changed;
-        changed = retainOnUpdate
-                ? mRetainedImplicitlyQueryable.add(recipientUid, visibleUid)
-                : mImplicitlyQueryable.add(recipientUid, visibleUid);
+        synchronized (mLock) {
+            changed = retainOnUpdate
+                    ? mRetainedImplicitlyQueryable.add(recipientUid, visibleUid)
+                    : mImplicitlyQueryable.add(recipientUid, visibleUid);
+        }
         if (changed && DEBUG_LOGGING) {
             Slog.i(TAG, (retainOnUpdate ? "retained " : "") + "implicit access granted: "
                     + recipientUid + " -> " + visibleUid);
@@ -758,9 +750,7 @@
                 mShouldFilterCache.put(recipientUid, visibleUid, false);
             }
         }
-        if (changed) {
-            onChanged();
-        }
+        onChanged();
         return changed;
     }
 
@@ -788,7 +778,7 @@
                 // let's first remove any prior rules for this package
                 removePackage(newPkgSetting, true /*isReplace*/);
             }
-            mStateProvider.runWithState((settings, users) -> {
+            mStateProvider.runWithState((settings, sharedUserSettings, users) -> {
                 ArraySet<String> additionalChangedPackages =
                         addPackageInternal(newPkgSetting, settings);
                 if (mSystemReady) {
@@ -806,9 +796,8 @@
                                 continue;
                             }
 
-                            updateShouldFilterCacheForPackage(null,
-                                    changedPkgSetting, settings, users, USER_ALL,
-                                    settings.size());
+                            updateShouldFilterCacheForPackage(null, changedPkgSetting,
+                                    settings, users, USER_ALL, settings.size());
                         }
                     }
                 } // else, rebuild entire cache when system is ready
@@ -835,7 +824,9 @@
             // packages for signature matches
             for (PackageStateInternal setting : existingSettings.values()) {
                 if (isSystemSigned(mSystemSigningDetails, setting)) {
-                    mForceQueryable.add(setting.getAppId());
+                    synchronized (mLock) {
+                        mForceQueryable.add(setting.getAppId());
+                    }
                 }
             }
         }
@@ -845,75 +836,76 @@
             return null;
         }
 
-        if (mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts())) {
-            mQueriesViaComponentRequireRecompute = true;
-        }
+        synchronized (mLock) {
+            if (mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts())) {
+                mQueriesViaComponentRequireRecompute = true;
+            }
 
-        final boolean newIsForceQueryable =
-                mForceQueryable.contains(newPkgSetting.getAppId())
-                        /* shared user that is already force queryable */
-                        || newPkgSetting.isForceQueryableOverride() /* adb override */
-                        || (newPkgSetting.isSystem() && (mSystemAppsQueryable
-                        || newPkg.isForceQueryable()
-                        || ArrayUtils.contains(mForceQueryableByDevicePackageNames,
-                        newPkg.getPackageName())));
-        if (newIsForceQueryable
-                || (mSystemSigningDetails != null
-                && isSystemSigned(mSystemSigningDetails, newPkgSetting))) {
-            mForceQueryable.add(newPkgSetting.getAppId());
-        }
+            final boolean newIsForceQueryable =
+                    mForceQueryable.contains(newPkgSetting.getAppId())
+                            /* shared user that is already force queryable */
+                            || newPkgSetting.isForceQueryableOverride() /* adb override */
+                            || (newPkgSetting.isSystem() && (mSystemAppsQueryable
+                            || newPkg.isForceQueryable()
+                            || ArrayUtils.contains(mForceQueryableByDevicePackageNames,
+                            newPkg.getPackageName())));
+            if (newIsForceQueryable
+                    || (mSystemSigningDetails != null
+                    && isSystemSigned(mSystemSigningDetails, newPkgSetting))) {
+                mForceQueryable.add(newPkgSetting.getAppId());
+            }
 
-        for (int i = existingSettings.size() - 1; i >= 0; i--) {
-            final PackageStateInternal existingSetting = existingSettings.valueAt(i);
-            if (existingSetting.getAppId() == newPkgSetting.getAppId()
-                    || existingSetting.getPkg()
-                    == null) {
-                continue;
-            }
-            final AndroidPackage existingPkg = existingSetting.getPkg();
-            // let's evaluate the ability of already added packages to see this new package
-            if (!newIsForceQueryable) {
-                if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(existingPkg,
-                        newPkg, mProtectedBroadcasts)) {
-                    mQueriesViaComponent.add(existingSetting.getAppId(),
-                            newPkgSetting.getAppId());
+            for (int i = existingSettings.size() - 1; i >= 0; i--) {
+                final PackageStateInternal existingSetting = existingSettings.valueAt(i);
+                if (existingSetting.getAppId() == newPkgSetting.getAppId()
+                        || existingSetting.getPkg()
+                        == null) {
+                    continue;
                 }
-                if (canQueryViaPackage(existingPkg, newPkg)
-                        || canQueryAsInstaller(existingSetting, newPkg)) {
-                    mQueriesViaPackage.add(existingSetting.getAppId(),
-                            newPkgSetting.getAppId());
+                final AndroidPackage existingPkg = existingSetting.getPkg();
+                // let's evaluate the ability of already added packages to see this new package
+                if (!newIsForceQueryable) {
+                    if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(existingPkg,
+                            newPkg, mProtectedBroadcasts)) {
+                        mQueriesViaComponent.add(existingSetting.getAppId(),
+                                newPkgSetting.getAppId());
+                    }
+                    if (canQueryViaPackage(existingPkg, newPkg)
+                            || canQueryAsInstaller(existingSetting, newPkg)) {
+                        mQueriesViaPackage.add(existingSetting.getAppId(),
+                                newPkgSetting.getAppId());
+                    }
+                    if (canQueryViaUsesLibrary(existingPkg, newPkg)) {
+                        mQueryableViaUsesLibrary.add(existingSetting.getAppId(),
+                                newPkgSetting.getAppId());
+                    }
                 }
-                if (canQueryViaUsesLibrary(existingPkg, newPkg)) {
-                    mQueryableViaUsesLibrary.add(existingSetting.getAppId(),
-                            newPkgSetting.getAppId());
+                // now we'll evaluate our new package's ability to see existing packages
+                if (!mForceQueryable.contains(existingSetting.getAppId())) {
+                    if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(newPkg,
+                            existingPkg, mProtectedBroadcasts)) {
+                        mQueriesViaComponent.add(newPkgSetting.getAppId(),
+                                existingSetting.getAppId());
+                    }
+                    if (canQueryViaPackage(newPkg, existingPkg)
+                            || canQueryAsInstaller(newPkgSetting, existingPkg)) {
+                        mQueriesViaPackage.add(newPkgSetting.getAppId(),
+                                existingSetting.getAppId());
+                    }
+                    if (canQueryViaUsesLibrary(newPkg, existingPkg)) {
+                        mQueryableViaUsesLibrary.add(newPkgSetting.getAppId(),
+                                existingSetting.getAppId());
+                    }
                 }
-            }
-            // now we'll evaluate our new package's ability to see existing packages
-            if (!mForceQueryable.contains(existingSetting.getAppId())) {
-                if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(newPkg,
-                        existingPkg, mProtectedBroadcasts)) {
-                    mQueriesViaComponent.add(newPkgSetting.getAppId(),
-                            existingSetting.getAppId());
+                // if either package instruments the other, mark both as visible to one another
+                if (newPkgSetting.getPkg() != null && existingSetting.getPkg() != null
+                        && (pkgInstruments(newPkgSetting.getPkg(), existingSetting.getPkg())
+                        || pkgInstruments(existingSetting.getPkg(), newPkgSetting.getPkg()))) {
+                    mQueriesViaPackage.add(newPkgSetting.getAppId(), existingSetting.getAppId());
+                    mQueriesViaPackage.add(existingSetting.getAppId(), newPkgSetting.getAppId());
                 }
-                if (canQueryViaPackage(newPkg, existingPkg)
-                        || canQueryAsInstaller(newPkgSetting, existingPkg)) {
-                    mQueriesViaPackage.add(newPkgSetting.getAppId(),
-                            existingSetting.getAppId());
-                }
-                if (canQueryViaUsesLibrary(newPkg, existingPkg)) {
-                    mQueryableViaUsesLibrary.add(newPkgSetting.getAppId(),
-                            existingSetting.getAppId());
-                }
-            }
-            // if either package instruments the other, mark both as visible to one another
-            if (newPkgSetting.getPkg() != null && existingSetting.getPkg() != null
-                    && (pkgInstruments(newPkgSetting.getPkg(), existingSetting.getPkg())
-                    || pkgInstruments(existingSetting.getPkg(), newPkgSetting.getPkg()))) {
-                mQueriesViaPackage.add(newPkgSetting.getAppId(), existingSetting.getAppId());
-                mQueriesViaPackage.add(existingSetting.getAppId(), newPkgSetting.getAppId());
             }
         }
-
         int existingSize = existingSettings.size();
         ArrayMap<String, AndroidPackage> existingPkgs = new ArrayMap<>(existingSize);
         for (int index = 0; index < existingSize; index++) {
@@ -954,7 +946,7 @@
     }
 
     private void updateEntireShouldFilterCache(int subjectUserId) {
-        mStateProvider.runWithState((settings, users) -> {
+        mStateProvider.runWithState((settings, sharedUserSettings, users) -> {
             int userId = USER_NULL;
             for (int u = 0; u < users.length; u++) {
                 if (subjectUserId == users[u].id) {
@@ -969,10 +961,12 @@
             }
             updateEntireShouldFilterCacheInner(settings, users, userId);
         });
+        onChanged();
     }
 
     private void updateEntireShouldFilterCacheInner(
-            ArrayMap<String, ? extends PackageStateInternal> settings, UserInfo[] users,
+            ArrayMap<String, ? extends PackageStateInternal> settings,
+            UserInfo[] users,
             int subjectUserId) {
         synchronized (mCacheLock) {
             if (subjectUserId == USER_ALL) {
@@ -982,16 +976,18 @@
         }
         for (int i = settings.size() - 1; i >= 0; i--) {
             updateShouldFilterCacheForPackage(
-                    null /*skipPackage*/, settings.valueAt(i), settings, users, subjectUserId, i);
+                    null /*skipPackage*/, settings.valueAt(i), settings, users,
+                    subjectUserId, i);
         }
     }
 
     private void updateEntireShouldFilterCacheAsync() {
         mBackgroundExecutor.execute(() -> {
             final ArrayMap<String, PackageStateInternal> settingsCopy = new ArrayMap<>();
+            final Collection<SharedUserSetting> sharedUserSettingsCopy = new ArraySet<>();
             final ArrayMap<String, AndroidPackage> packagesCache = new ArrayMap<>();
             final UserInfo[][] usersRef = new UserInfo[1][];
-            mStateProvider.runWithState((settings, users) -> {
+            mStateProvider.runWithState((settings, sharedUserSettings, users) -> {
                 packagesCache.ensureCapacity(settings.size());
                 settingsCopy.putAll(settings);
                 usersRef[0] = users;
@@ -1001,11 +997,12 @@
                     final AndroidPackage pkg = settings.valueAt(i).getPkg();
                     packagesCache.put(settings.keyAt(i), pkg);
                 }
+                sharedUserSettingsCopy.addAll(sharedUserSettings);
             });
 
             boolean[] changed = new boolean[1];
             // We have a cache, let's make sure the world hasn't changed out from under us.
-            mStateProvider.runWithState((settings, users) -> {
+            mStateProvider.runWithState((settings, sharedUserSettings, users) -> {
                 if (settings.size() != settingsCopy.size()) {
                     changed[0] = true;
                     return;
@@ -1025,7 +1022,9 @@
                     Slog.i(TAG, "Rebuilding cache with lock due to package change.");
                 }
             } else {
-                updateEntireShouldFilterCacheInner(settingsCopy, usersRef[0], USER_ALL);
+                updateEntireShouldFilterCacheInner(settingsCopy,
+                        usersRef[0], USER_ALL);
+                onChanged();
             }
         });
     }
@@ -1035,7 +1034,6 @@
             return;
         }
         updateEntireShouldFilterCache(newUserId);
-        onChanged();
     }
 
     public void onUserDeleted(@UserIdInt int userId) {
@@ -1047,7 +1045,7 @@
     }
 
     private void updateShouldFilterCacheForPackage(String packageName) {
-        mStateProvider.runWithState((settings, users) -> {
+        mStateProvider.runWithState((settings, sharedUserSettings, users) -> {
             if (!mSystemReady) {
                 return;
             }
@@ -1055,6 +1053,7 @@
                     settings.get(packageName), settings, users, USER_ALL,
                     settings.size() /*maxIndex*/);
         });
+        onChanged();
     }
 
     private void updateShouldFilterCacheForPackage(
@@ -1134,16 +1133,18 @@
     private void collectProtectedBroadcasts(
             ArrayMap<String, ? extends PackageStateInternal> existingSettings,
             @Nullable String excludePackage) {
-        mProtectedBroadcasts.clear();
-        for (int i = existingSettings.size() - 1; i >= 0; i--) {
-            PackageStateInternal setting = existingSettings.valueAt(i);
-            if (setting.getPkg() == null || setting.getPkg().getPackageName().equals(
-                    excludePackage)) {
-                continue;
-            }
-            final List<String> protectedBroadcasts = setting.getPkg().getProtectedBroadcasts();
-            if (!protectedBroadcasts.isEmpty()) {
-                mProtectedBroadcasts.addAll(protectedBroadcasts);
+        synchronized (mLock) {
+            mProtectedBroadcasts.clear();
+            for (int i = existingSettings.size() - 1; i >= 0; i--) {
+                PackageStateInternal setting = existingSettings.valueAt(i);
+                if (setting.getPkg() == null || setting.getPkg().getPackageName().equals(
+                        excludePackage)) {
+                    continue;
+                }
+                final List<String> protectedBroadcasts = setting.getPkg().getProtectedBroadcasts();
+                if (!protectedBroadcasts.isEmpty()) {
+                    mProtectedBroadcasts.addAll(protectedBroadcasts);
+                }
             }
         }
     }
@@ -1154,24 +1155,26 @@
      */
     private void recomputeComponentVisibility(
             ArrayMap<String, ? extends PackageStateInternal> existingSettings) {
-        mQueriesViaComponent.clear();
-        for (int i = existingSettings.size() - 1; i >= 0; i--) {
-            PackageStateInternal setting = existingSettings.valueAt(i);
-            if (setting.getPkg() == null || requestsQueryAllPackages(setting.getPkg())) {
-                continue;
-            }
-            for (int j = existingSettings.size() - 1; j >= 0; j--) {
-                if (i == j) {
+        synchronized (mLock) {
+            mQueriesViaComponent.clear();
+            for (int i = existingSettings.size() - 1; i >= 0; i--) {
+                PackageStateInternal setting = existingSettings.valueAt(i);
+                if (setting.getPkg() == null || requestsQueryAllPackages(setting.getPkg())) {
                     continue;
                 }
-                final PackageStateInternal otherSetting = existingSettings.valueAt(j);
-                if (otherSetting.getPkg() == null || mForceQueryable.contains(
-                        otherSetting.getAppId())) {
-                    continue;
-                }
-                if (canQueryViaComponents(setting.getPkg(), otherSetting.getPkg(),
-                        mProtectedBroadcasts)) {
-                    mQueriesViaComponent.add(setting.getAppId(), otherSetting.getAppId());
+                for (int j = existingSettings.size() - 1; j >= 0; j--) {
+                    if (i == j) {
+                        continue;
+                    }
+                    final PackageStateInternal otherSetting = existingSettings.valueAt(j);
+                    if (otherSetting.getPkg() == null || mForceQueryable.contains(
+                            otherSetting.getAppId())) {
+                        continue;
+                    }
+                    if (canQueryViaComponents(setting.getPkg(), otherSetting.getPkg(),
+                            mProtectedBroadcasts)) {
+                        mQueriesViaComponent.add(setting.getAppId(), otherSetting.getAppId());
+                    }
                 }
             }
         }
@@ -1185,8 +1188,10 @@
     @Nullable
     public SparseArray<int[]> getVisibilityAllowList(PackageStateInternal setting, int[] users,
             ArrayMap<String, ? extends PackageStateInternal> existingSettings) {
-        if (mForceQueryable.contains(setting.getAppId())) {
-            return null;
+        synchronized (mLock) {
+            if (mForceQueryable.contains(setting.getAppId())) {
+                return null;
+            }
         }
         // let's reserve max memory to limit the number of allocations
         SparseArray<int[]> result = new SparseArray<>(users.length);
@@ -1249,57 +1254,59 @@
      * @param isReplace if the package is being replaced.
      */
     public void removePackage(PackageStateInternal setting, boolean isReplace) {
-        mStateProvider.runWithState((settings, users) -> {
+        mStateProvider.runWithState((settings, sharedUserSettings, users) -> {
             final ArraySet<String> additionalChangedPackages;
             final int userCount = users.length;
-            for (int u = 0; u < userCount; u++) {
-                final int userId = users[u].id;
-                final int removingUid = UserHandle.getUid(userId, setting.getAppId());
-                mImplicitlyQueryable.remove(removingUid);
-                for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) {
-                    mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i),
-                            removingUid);
+            synchronized (mLock) {
+                for (int u = 0; u < userCount; u++) {
+                    final int userId = users[u].id;
+                    final int removingUid = UserHandle.getUid(userId, setting.getAppId());
+                    mImplicitlyQueryable.remove(removingUid);
+                    for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) {
+                        mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i),
+                                removingUid);
+                    }
+
+                    if (isReplace) {
+                        continue;
+                    }
+
+                    mRetainedImplicitlyQueryable.remove(removingUid);
+                    for (int i = mRetainedImplicitlyQueryable.size() - 1; i >= 0; i--) {
+                        mRetainedImplicitlyQueryable.remove(
+                                mRetainedImplicitlyQueryable.keyAt(i), removingUid);
+                    }
                 }
 
-                if (isReplace) {
-                    continue;
+                if (!mQueriesViaComponentRequireRecompute) {
+                    mQueriesViaComponent.remove(setting.getAppId());
+                    for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) {
+                        mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i),
+                                setting.getAppId());
+                    }
                 }
-
-                mRetainedImplicitlyQueryable.remove(removingUid);
-                for (int i = mRetainedImplicitlyQueryable.size() - 1; i >= 0; i--) {
-                    mRetainedImplicitlyQueryable.remove(
-                            mRetainedImplicitlyQueryable.keyAt(i), removingUid);
-                }
-            }
-
-            if (!mQueriesViaComponentRequireRecompute) {
-                mQueriesViaComponent.remove(setting.getAppId());
-                for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) {
-                    mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i),
+                mQueriesViaPackage.remove(setting.getAppId());
+                for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) {
+                    mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i),
                             setting.getAppId());
                 }
-            }
-            mQueriesViaPackage.remove(setting.getAppId());
-            for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) {
-                mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i),
-                        setting.getAppId());
-            }
-            mQueryableViaUsesLibrary.remove(setting.getAppId());
-            for (int i = mQueryableViaUsesLibrary.size() - 1; i >= 0; i--) {
-                mQueryableViaUsesLibrary.remove(mQueryableViaUsesLibrary.keyAt(i),
-                        setting.getAppId());
-            }
+                mQueryableViaUsesLibrary.remove(setting.getAppId());
+                for (int i = mQueryableViaUsesLibrary.size() - 1; i >= 0; i--) {
+                    mQueryableViaUsesLibrary.remove(mQueryableViaUsesLibrary.keyAt(i),
+                            setting.getAppId());
+                }
 
-            mForceQueryable.remove(setting.getAppId());
+                mForceQueryable.remove(setting.getAppId());
 
-            if (setting.getPkg() != null
-                    && !setting.getPkg().getProtectedBroadcasts().isEmpty()) {
-                final String removingPackageName = setting.getPkg().getPackageName();
-                final ArrayList<String> protectedBroadcasts = new ArrayList<>();
-                protectedBroadcasts.addAll(mProtectedBroadcasts.untrackedStorage());
-                collectProtectedBroadcasts(settings, removingPackageName);
-                if (!mProtectedBroadcasts.containsAll(protectedBroadcasts)) {
-                    mQueriesViaComponentRequireRecompute = true;
+                if (setting.getPkg() != null
+                        && !setting.getPkg().getProtectedBroadcasts().isEmpty()) {
+                    final String removingPackageName = setting.getPkg().getPackageName();
+                    final ArrayList<String> protectedBroadcasts = new ArrayList<>();
+                    protectedBroadcasts.addAll(mProtectedBroadcasts.untrackedStorage());
+                    collectProtectedBroadcasts(settings, removingPackageName);
+                    if (!mProtectedBroadcasts.containsAll(protectedBroadcasts)) {
+                        mQueriesViaComponentRequireRecompute = true;
+                    }
                 }
             }
 
@@ -1314,8 +1321,8 @@
             // update the
             //       cache
             if (setting.hasSharedUser()) {
-                final ArraySet<PackageStateInternal> sharedUserPackages =
-                        mPmInternal.getSharedUserPackages(setting.getSharedUserAppId());
+                final ArraySet<? extends PackageStateInternal> sharedUserPackages =
+                        getSharedUserPackages(setting.getSharedUserAppId(), sharedUserSettings);
                 for (int i = sharedUserPackages.size() - 1; i >= 0; i--) {
                     if (sharedUserPackages.valueAt(i) == setting) {
                         continue;
@@ -1327,8 +1334,8 @@
 
             removeAppIdFromVisibilityCache(setting.getAppId());
             if (mSystemReady && setting.hasSharedUser()) {
-                final ArraySet<PackageStateInternal> sharedUserPackages =
-                        mPmInternal.getSharedUserPackages(setting.getSharedUserAppId());
+                final ArraySet<? extends PackageStateInternal> sharedUserPackages =
+                        getSharedUserPackages(setting.getSharedUserAppId(), sharedUserSettings);
                 for (int i = sharedUserPackages.size() - 1; i >= 0; i--) {
                     PackageStateInternal siblingSetting =
                             sharedUserPackages.valueAt(i);
@@ -1336,8 +1343,8 @@
                         continue;
                     }
                     updateShouldFilterCacheForPackage(
-                            setting.getPackageName(), siblingSetting, settings, users,
-                            USER_ALL, settings.size());
+                            setting.getPackageName(), siblingSetting, settings,
+                            users, USER_ALL, settings.size());
                 }
             }
 
@@ -1353,14 +1360,24 @@
                             continue;
                         }
 
-                        updateShouldFilterCacheForPackage(null,
-                                changedPkgSetting, settings, users, USER_ALL, settings.size());
+                        updateShouldFilterCacheForPackage(null, changedPkgSetting,
+                                settings, users, USER_ALL, settings.size());
                     }
                 }
             }
-
-            onChanged();
         });
+        onChanged();
+    }
+
+    private ArraySet<? extends PackageStateInternal> getSharedUserPackages(int sharedUserAppId,
+            Collection<SharedUserSetting> sharedUserSettings) {
+        for (SharedUserSetting setting : sharedUserSettings) {
+            if (setting.mAppId != sharedUserAppId) {
+                continue;
+            }
+            return setting.getPackageStates();
+        }
+        return new ArraySet<>();
     }
 
     /**
@@ -1441,23 +1458,26 @@
                 return true;
             }
             final PackageStateInternal callingPkgSetting;
-            final ArraySet<? extends PackageStateInternal> callingSharedPkgSettings;
             if (DEBUG_TRACING) {
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingSetting instanceof");
             }
+            final ArraySet<PackageStateInternal> callingSharedPkgSettings = new ArraySet<>();
+
             if (callingSetting instanceof PackageStateInternal) {
                 final PackageStateInternal packageState = (PackageStateInternal) callingSetting;
                 if (packageState.hasSharedUser()) {
                     callingPkgSetting = null;
-                    callingSharedPkgSettings = mPmInternal.getSharedUserPackages(
-                            packageState.getSharedUserAppId());
+                    mStateProvider.runWithState((settings, sharedUserSettings, users) ->
+                            callingSharedPkgSettings.addAll(getSharedUserPackages(
+                                    packageState.getSharedUserAppId(), sharedUserSettings)));
+
                 } else {
                     callingPkgSetting = packageState;
-                    callingSharedPkgSettings = null;
                 }
             } else {
                 callingPkgSetting = null;
-                callingSharedPkgSettings = ((SharedUserSetting) callingSetting).getPackageStates();
+                callingSharedPkgSettings.addAll(
+                        ((SharedUserSetting) callingSetting).getPackageStates());
             }
             if (DEBUG_TRACING) {
                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -1545,11 +1565,13 @@
                 if (DEBUG_TRACING) {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mForceQueryable");
                 }
-                if (mForceQueryable.contains(targetAppId)) {
-                    if (DEBUG_LOGGING) {
-                        log(callingSetting, targetPkgSetting, "force queryable");
+                synchronized (mLock) {
+                    if (mForceQueryable.contains(targetAppId)) {
+                        if (DEBUG_LOGGING) {
+                            log(callingSetting, targetPkgSetting, "force queryable");
+                        }
+                        return false;
                     }
-                    return false;
                 }
             } finally {
                 if (DEBUG_TRACING) {
@@ -1560,11 +1582,13 @@
                 if (DEBUG_TRACING) {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaPackage");
                 }
-                if (mQueriesViaPackage.contains(callingAppId, targetAppId)) {
-                    if (DEBUG_LOGGING) {
-                        log(callingSetting, targetPkgSetting, "queries package");
+                synchronized (mLock) {
+                    if (mQueriesViaPackage.contains(callingAppId, targetAppId)) {
+                        if (DEBUG_LOGGING) {
+                            log(callingSetting, targetPkgSetting, "queries package");
+                        }
+                        return false;
                     }
-                    return false;
                 }
             } finally {
                 if (DEBUG_TRACING) {
@@ -1576,15 +1600,20 @@
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaComponent");
                 }
                 if (mQueriesViaComponentRequireRecompute) {
-                    mStateProvider.runWithState((settings, users) -> {
-                        recomputeComponentVisibility(settings);
+                    final ArrayMap<String, PackageStateInternal> settingsCopy = new ArrayMap<>();
+                    mStateProvider.runWithState((settings, sharedUserSettings, users) -> {
+                        settingsCopy.putAll(settings);
                     });
+                    recomputeComponentVisibility(settingsCopy);
+                    onChanged();
                 }
-                if (mQueriesViaComponent.contains(callingAppId, targetAppId)) {
-                    if (DEBUG_LOGGING) {
-                        log(callingSetting, targetPkgSetting, "queries component");
+                synchronized (mLock) {
+                    if (mQueriesViaComponent.contains(callingAppId, targetAppId)) {
+                        if (DEBUG_LOGGING) {
+                            log(callingSetting, targetPkgSetting, "queries component");
+                        }
+                        return false;
                     }
-                    return false;
                 }
             } finally {
                 if (DEBUG_TRACING) {
@@ -1597,11 +1626,13 @@
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mImplicitlyQueryable");
                 }
                 final int targetUid = UserHandle.getUid(targetUserId, targetAppId);
-                if (mImplicitlyQueryable.contains(callingUid, targetUid)) {
-                    if (DEBUG_LOGGING) {
-                        log(callingSetting, targetPkgSetting, "implicitly queryable for user");
+                synchronized (mLock) {
+                    if (mImplicitlyQueryable.contains(callingUid, targetUid)) {
+                        if (DEBUG_LOGGING) {
+                            log(callingSetting, targetPkgSetting, "implicitly queryable for user");
+                        }
+                        return false;
                     }
-                    return false;
                 }
             } finally {
                 if (DEBUG_TRACING) {
@@ -1614,12 +1645,14 @@
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mRetainedImplicitlyQueryable");
                 }
                 final int targetUid = UserHandle.getUid(targetUserId, targetAppId);
-                if (mRetainedImplicitlyQueryable.contains(callingUid, targetUid)) {
-                    if (DEBUG_LOGGING) {
-                        log(callingSetting, targetPkgSetting,
-                                "retained implicitly queryable for user");
+                synchronized (mLock) {
+                    if (mRetainedImplicitlyQueryable.contains(callingUid, targetUid)) {
+                        if (DEBUG_LOGGING) {
+                            log(callingSetting, targetPkgSetting,
+                                    "retained implicitly queryable for user");
+                        }
+                        return false;
                     }
-                    return false;
                 }
             } finally {
                 if (DEBUG_TRACING) {
@@ -1632,7 +1665,7 @@
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mOverlayReferenceMapper");
                 }
                 final String targetName = targetPkg.getPackageName();
-                if (callingSharedPkgSettings != null) {
+                if (!callingSharedPkgSettings.isEmpty()) {
                     int size = callingSharedPkgSettings.size();
                     for (int index = 0; index < size; index++) {
                         PackageStateInternal pkgSetting = callingSharedPkgSettings.valueAt(index);
@@ -1665,11 +1698,13 @@
                 if (DEBUG_TRACING) {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueryableViaUsesLibrary");
                 }
-                if (mQueryableViaUsesLibrary.contains(callingAppId, targetAppId)) {
-                    if (DEBUG_LOGGING) {
-                        log(callingSetting, targetPkgSetting, "queryable for library users");
+                synchronized (mLock) {
+                    if (mQueryableViaUsesLibrary.contains(callingAppId, targetAppId)) {
+                        if (DEBUG_LOGGING) {
+                            log(callingSetting, targetPkgSetting, "queryable for library users");
+                        }
+                        return false;
                     }
-                    return false;
                 }
             } finally {
                 if (DEBUG_TRACING) {
@@ -1785,23 +1820,25 @@
         pw.println("  system apps queryable: " + mSystemAppsQueryable);
         dumpPackageSet(pw, filteringAppId, mForceQueryable.untrackedStorage(),
                 "forceQueryable", "  ", expandPackages);
-        pw.println("  queries via package name:");
-        dumpQueriesMap(pw, filteringAppId, mQueriesViaPackage, "    ", expandPackages);
-        pw.println("  queries via component:");
-        dumpQueriesMap(pw, filteringAppId, mQueriesViaComponent, "    ", expandPackages);
-        pw.println("  queryable via interaction:");
-        for (int user : users) {
-            pw.append("    User ").append(Integer.toString(user)).println(":");
-            dumpQueriesMap(pw,
-                    filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId),
-                    mImplicitlyQueryable, "      ", expandPackages);
-            dumpQueriesMap(pw,
-                    filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId),
-                    mRetainedImplicitlyQueryable, "      ", expandPackages);
+        synchronized (mLock) {
+            pw.println("  queries via package name:");
+            dumpQueriesMap(pw, filteringAppId, mQueriesViaPackage, "    ", expandPackages);
+            pw.println("  queries via component:");
+            dumpQueriesMap(pw, filteringAppId, mQueriesViaComponent, "    ", expandPackages);
+            pw.println("  queryable via interaction:");
+            for (int user : users) {
+                pw.append("    User ").append(Integer.toString(user)).println(":");
+                dumpQueriesMap(pw,
+                        filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId),
+                        mImplicitlyQueryable, "      ", expandPackages);
+                dumpQueriesMap(pw,
+                        filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId),
+                        mRetainedImplicitlyQueryable, "      ", expandPackages);
+            }
+            pw.println("  queryable via uses-library:");
+            dumpQueriesMap(pw, filteringAppId, mQueryableViaUsesLibrary, "    ",
+                    expandPackages);
         }
-        pw.println("  queryable via uses-library:");
-        dumpQueriesMap(pw, filteringAppId, mQueryableViaUsesLibrary, "    ",
-                expandPackages);
     }
 
     private static void dumpQueriesMap(PrintWriter pw, @Nullable Integer filteringId,
diff --git a/services/core/java/com/android/server/pm/ChangedPackagesTracker.java b/services/core/java/com/android/server/pm/ChangedPackagesTracker.java
index bd12981..3802135 100644
--- a/services/core/java/com/android/server/pm/ChangedPackagesTracker.java
+++ b/services/core/java/com/android/server/pm/ChangedPackagesTracker.java
@@ -90,25 +90,27 @@
     }
 
     void updateSequenceNumber(@NonNull String packageName, int[] userList) {
-        for (int i = userList.length - 1; i >= 0; --i) {
-            final int userId = userList[i];
-            SparseArray<String> changedPackages = mUserIdToSequenceToPackage.get(userId);
-            if (changedPackages == null) {
-                changedPackages = new SparseArray<>();
-                mUserIdToSequenceToPackage.put(userId, changedPackages);
+        synchronized (mLock) {
+            for (int i = userList.length - 1; i >= 0; --i) {
+                final int userId = userList[i];
+                SparseArray<String> changedPackages = mUserIdToSequenceToPackage.get(userId);
+                if (changedPackages == null) {
+                    changedPackages = new SparseArray<>();
+                    mUserIdToSequenceToPackage.put(userId, changedPackages);
+                }
+                Map<String, Integer> sequenceNumbers = mChangedPackagesSequenceNumbers.get(userId);
+                if (sequenceNumbers == null) {
+                    sequenceNumbers = new HashMap<>();
+                    mChangedPackagesSequenceNumbers.put(userId, sequenceNumbers);
+                }
+                final Integer sequenceNumber = sequenceNumbers.get(packageName);
+                if (sequenceNumber != null) {
+                    changedPackages.remove(sequenceNumber);
+                }
+                changedPackages.put(mChangedPackagesSequenceNumber, packageName);
+                sequenceNumbers.put(packageName, mChangedPackagesSequenceNumber);
             }
-            Map<String, Integer> sequenceNumbers = mChangedPackagesSequenceNumbers.get(userId);
-            if (sequenceNumbers == null) {
-                sequenceNumbers = new HashMap<>();
-                mChangedPackagesSequenceNumbers.put(userId, sequenceNumbers);
-            }
-            final Integer sequenceNumber = sequenceNumbers.get(packageName);
-            if (sequenceNumber != null) {
-                changedPackages.remove(sequenceNumber);
-            }
-            changedPackages.put(mChangedPackagesSequenceNumber, packageName);
-            sequenceNumbers.put(packageName, mChangedPackagesSequenceNumber);
+            mChangedPackagesSequenceNumber++;
         }
-        mChangedPackagesSequenceNumber++;
     }
 }
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 7dae22a..d3d291e 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -108,7 +108,7 @@
     }
 
     /**
-     *  This method is an internal method that could be get invoked either
+     *  This method is an internal method that could be invoked either
      *  to delete an installed package or to clean up a failed installation.
      *  After deleting an installed package, a broadcast is sent to notify any
      *  listeners that the package has been removed. For cleaning up a failed
@@ -146,6 +146,8 @@
         int[] allUsers;
         final int freezeUser;
         final SparseArray<TempUserState> priorUserStates;
+
+        final boolean isInstallerPackage;
         /** enabled state of the uninstalled application */
         synchronized (mPm.mLock) {
             final Computer computer = mPm.snapshotComputer();
@@ -226,6 +228,8 @@
                 freezeUser = removeUser;
                 priorUserStates = null;
             }
+
+            isInstallerPackage = mPm.mSettings.isInstallerPackage(packageName);
         }
 
         synchronized (mPm.mInstallLock) {
@@ -324,6 +328,12 @@
             }
         }
 
+        if (res && isInstallerPackage) {
+            final PackageInstallerService packageInstallerService =
+                    mPm.mInjector.getPackageInstallerService();
+            packageInstallerService.onInstallerPackageDeleted(uninstalledPs.getAppId(), removeUser);
+        }
+
         return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR;
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 9b5984e..e406a1a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -861,6 +861,7 @@
         synchronized (mSessions) {
             mSessions.put(sessionId, session);
         }
+        mPm.addInstallerPackageName(session.getInstallSource());
 
         mCallbacks.notifySessionCreated(session.sessionId, session.userId);
 
@@ -1735,4 +1736,37 @@
                 .setPackage(sessionInfo.installerPackageName);
         mContext.sendBroadcastAsUser(sessionUpdatedIntent, UserHandle.of(userId));
     }
+
+    /**
+     * Abandon unfinished sessions if the installer package has been uninstalled.
+     * @param installerAppId the app ID of the installer package that has been uninstalled.
+     * @param userId the user that has the installer package uninstalled.
+     */
+    void onInstallerPackageDeleted(int installerAppId, int userId) {
+        synchronized (mSessions) {
+            for (int i = 0; i < mSessions.size(); i++) {
+                final PackageInstallerSession session = mSessions.valueAt(i);
+                if (!matchesInstaller(session, installerAppId, userId)) {
+                    continue;
+                }
+                // Find parent session and only abandon parent session if installer matches
+                PackageInstallerSession root = !session.hasParentSessionId()
+                        ? session : mSessions.get(session.getParentSessionId());
+                if (root != null && matchesInstaller(root, installerAppId, userId)
+                        && !root.isDestroyed()) {
+                    root.abandon();
+                }
+            }
+        }
+    }
+
+    private boolean matchesInstaller(PackageInstallerSession session, int installerAppId,
+            int userId) {
+        final int installerUid = session.getInstallerUid();
+        if (installerAppId == UserHandle.USER_ALL) {
+            return UserHandle.getAppId(installerUid) == installerAppId;
+        } else {
+            return UserHandle.getUid(userId, installerAppId) == installerUid;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ce1ee70..ffd924e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1543,6 +1543,7 @@
     }
 
     // Link watchables to the class
+    @SuppressWarnings("GuardedBy")
     private void registerObservers(boolean verify) {
         // Null check to handle nullable test parameters
         if (mPackages != null) {
@@ -2256,7 +2257,7 @@
 
     @GuardedBy("mLock")
     void updateInstantAppInstallerLocked(String modifiedPackage) {
-        // we're only interested in updating the installer appliction when 1) it's not
+        // we're only interested in updating the installer application when 1) it's not
         // already set or 2) the modified package is the installer
         if (mInstantAppInstallerActivity != null
                 && !mInstantAppInstallerActivity.getComponentName().getPackageName()
@@ -2740,7 +2741,6 @@
         return mModuleInfoProvider.getModuleInfo(packageName, flags);
     }
 
-    @GuardedBy("mLock")
     void updateSequenceNumberLP(PackageSetting pkgSetting, int[] userList) {
         mChangedPackagesTracker.updateSequenceNumber(pkgSetting.getPackageName(), userList);
     }
@@ -3074,8 +3074,8 @@
                 userId);
     }
 
-    private void enforceCanSetDistractingPackageRestrictionsAsUser(@NonNull Computer snapshot,
-            int callingUid, int userId, String callingMethod) {
+    private void enforceCanSetDistractingPackageRestrictionsAsUser(int callingUid, int userId,
+            String callingMethod) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
                 callingMethod);
 
@@ -3159,6 +3159,7 @@
         }
     }
 
+    @SuppressWarnings("GuardedBy")
     VersionInfo getSettingsVersionForPackage(AndroidPackage pkg) {
         if (pkg.isExternalStorage()) {
             if (TextUtils.isEmpty(pkg.getVolumeUuid())) {
@@ -3288,6 +3289,7 @@
      * Update component enabled settings to {@link PackageManager#COMPONENT_ENABLED_STATE_DEFAULT}
      * if the resetEnabledSettingsOnAppDataCleared is {@code true}.
      */
+    @GuardedBy("mLock")
     private void resetComponentEnabledSettingsIfNeededLPw(String packageName, int userId) {
         final AndroidPackage pkg = packageName != null ? mPackages.get(packageName) : null;
         if (pkg == null || !pkg.isResetEnabledSettingsOnAppDataCleared()) {
@@ -3879,6 +3881,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private boolean setEnabledSettingInternalLocked(@NonNull Computer computer,
             PackageSetting pkgSetting, ComponentEnabledSetting setting, @UserIdInt int userId,
             String callingPackage) {
@@ -4554,7 +4557,9 @@
                             final Computer snapshot = snapshotComputer();
                             unsuspendForSuspendingPackage(snapshot, packageName, userId);
                             removeAllDistractingPackageRestrictions(snapshot, userId);
-                            flushPackageRestrictionsAsUserInternalLocked(userId);
+                            synchronized (mLock) {
+                                flushPackageRestrictionsAsUserInternalLocked(userId);
+                            }
                         }
                     }
                     if (observer != null) {
@@ -4972,6 +4977,7 @@
         }
 
         @Override
+        @SuppressWarnings("GuardedBy")
         public int getRuntimePermissionsVersion(@UserIdInt int userId) {
             Preconditions.checkArgumentNonnegative(userId);
             enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
@@ -5576,7 +5582,7 @@
                 int restrictionFlags, int userId) {
             final int callingUid = Binder.getCallingUid();
             final Computer snapshot = snapshotComputer();
-            enforceCanSetDistractingPackageRestrictionsAsUser(snapshot, callingUid, userId,
+            enforceCanSetDistractingPackageRestrictionsAsUser(callingUid, userId,
                     "setDistractingPackageRestrictionsAsUser");
             Objects.requireNonNull(packageNames, "packageNames cannot be null");
             return mDistractingPackageHelper.setDistractingPackageRestrictionsAsUser(snapshot,
@@ -5845,6 +5851,7 @@
         }
 
         @Override
+        @SuppressWarnings("GuardedBy")
         public void setRuntimePermissionsVersion(int version, @UserIdInt int userId) {
             Preconditions.checkArgumentNonnegative(version);
             Preconditions.checkArgumentNonnegative(userId);
@@ -6363,6 +6370,7 @@
         }
 
         @Override
+        @SuppressWarnings("GuardedBy")
         public void updateRuntimePermissionsFingerprint(@UserIdInt int userId) {
             mSettings.updateRuntimePermissionsFingerprint(userId);
         }
@@ -6397,6 +6405,7 @@
         }
 
         @Override
+        @SuppressWarnings("GuardedBy")
         public boolean isPermissionUpgradeNeeded(int userId) {
             return mSettings.isPermissionUpgradeNeeded(userId);
         }
@@ -7168,4 +7177,10 @@
     void notifyInstantAppPackageInstalled(String packageName, int[] newUsers) {
         mInstantAppRegistry.onPackageInstalled(snapshotComputer(), packageName, newUsers);
     }
+
+    void addInstallerPackageName(InstallSource installSource) {
+        synchronized (mLock) {
+            mSettings.addInstallerPackageNames(installSource);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index b53cfc5..e6d59d4 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -380,8 +380,8 @@
     private final SnapshotCache<WatchedArrayMap<String, PackageSetting>> mPackagesSnapshot;
 
     /**
-     * List of packages that were involved in installing other packages, i.e. are listed
-     * in at least one app's InstallSource.
+     * List of packages that were involved in installing other packages, i.e. packages that created
+     * new sessions or are listed in at least one app's InstallSource.
      */
     @Watched
     private final WatchedArraySet<String> mInstallerPackages;
@@ -5923,4 +5923,8 @@
             }
         }
     }
+
+    boolean isInstallerPackage(@NonNull String packageName) {
+        return mInstallerPackages.contains(packageName);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 056f255..9627c43 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -105,6 +105,7 @@
 import android.util.Xml;
 import android.view.IWindowManager;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
@@ -455,6 +456,8 @@
 
     private final boolean mIsAppSearchEnabled;
 
+    private ComponentName mChooserActivity;
+
     static class InvalidFileFormatException extends Exception {
         public InvalidFileFormatException(String message, Throwable cause) {
             super(message, cause);
@@ -1646,6 +1649,26 @@
         return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
     }
 
+    @VisibleForTesting
+    ComponentName injectChooserActivity() {
+        if (mChooserActivity == null) {
+            mChooserActivity = ComponentName.unflattenFromString(
+                    mContext.getResources().getString(R.string.config_chooserActivity));
+        }
+        return mChooserActivity;
+    }
+
+    private boolean isCallerChooserActivity() {
+        // TODO(b/228975502): Migrate this check to a proper permission or role check
+        final int callingUid = injectBinderCallingUid();
+        ComponentName systemChooser = injectChooserActivity();
+        if (systemChooser == null) {
+            return false;
+        }
+        int uid = injectGetPackageUid(systemChooser.getPackageName(), UserHandle.USER_SYSTEM);
+        return uid == callingUid;
+    }
+
     private void enforceSystemOrShell() {
         if (!(isCallerSystem() || isCallerShell())) {
             throw new SecurityException("Caller must be system or shell");
@@ -2525,7 +2548,9 @@
             IntentFilter filter, @UserIdInt int userId) {
         Preconditions.checkStringNotEmpty(packageName, "packageName");
         Objects.requireNonNull(filter, "intentFilter");
-        verifyCaller(packageName, userId);
+        if (!isCallerChooserActivity()) {
+            verifyCaller(packageName, userId);
+        }
         enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS,
                 "getShareTargets");
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index 0e6d5e5..8dc9428 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -92,21 +92,6 @@
     public abstract void setDevicePolicyUserRestrictions(int originatingUserId,
             @Nullable Bundle global, @Nullable RestrictionsSet local, boolean isDeviceOwner);
 
-    /**
-     * Returns the "base" user restrictions.
-     *
-     * Used by {@link com.android.server.devicepolicy.DevicePolicyManagerService} for upgrading
-     * from MNC.
-     */
-    public abstract Bundle getBaseUserRestrictions(int userId);
-
-    /**
-     * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} for upgrading
-     * from MNC.
-     */
-    public abstract void setBaseUserRestrictionsByDpmsForMigration(int userId,
-            Bundle baseRestrictions);
-
     /** Return a user restriction. */
     public abstract boolean getUserRestriction(int userId, String key);
 
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index ee0fdc0..358e71a 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -4317,16 +4317,9 @@
 
     private long logUserCreateJourneyBegin(@UserIdInt int userId, String userType,
             @UserInfoFlag int flags) {
-        final long sessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE);
-        // log the journey atom with the user metadata
-        FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId,
+        return logUserJourneyBegin(
                 FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE,
-                /* origin_user= */ -1, userId, UserManager.getUserTypeForStatsd(userType), flags);
-        // log the event atom to indicate the event start
-        FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
-                FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER,
-                FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN);
-        return sessionId;
+                userId, userType, flags);
     }
 
     private void logUserCreateJourneyFinish(long sessionId, @UserIdInt int userId, boolean finish) {
@@ -4336,6 +4329,46 @@
                         : FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE);
     }
 
+    private long logUserRemoveJourneyBegin(@UserIdInt int userId, String userType,
+            @UserInfoFlag int flags) {
+        return logUserJourneyBegin(
+                FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE,
+                userId, userType, flags);
+    }
+
+    private void logUserRemoveJourneyFinish(long sessionId, @UserIdInt int userId, boolean finish) {
+        FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
+                FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER,
+                finish ? FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__FINISH
+                        : FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE);
+    }
+
+    private long logUserJourneyBegin(int journey, @UserIdInt int userId, String userType,
+            @UserInfoFlag int flags) {
+        final long sessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE);
+        // log the journey atom with the user metadata
+        FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId,
+                journey, /* origin_user= */ -1, userId,
+                UserManager.getUserTypeForStatsd(userType), flags);
+
+        // log the event atom to indicate the event start
+        int event;
+        switch (journey) {
+            case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE:
+                event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER;
+                break;
+            case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE:
+                event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER;
+                break;
+            default:
+                throw new IllegalArgumentException("Journey " + journey + " not expected.");
+        }
+
+        FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId,
+                event, FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN);
+        return sessionId;
+    }
+
     /** Register callbacks for statsd pulled atoms. */
     private void registerStatsCallbacks() {
         final StatsManager statsManager = mContext.getSystemService(StatsManager.class);
@@ -4578,6 +4611,10 @@
                 userData.info.flags |= UserInfo.FLAG_DISABLED;
                 writeUserLP(userData);
             }
+
+            final long sessionId = logUserRemoveJourneyBegin(
+                    userId, userData.info.userType, userData.info.flags);
+
             try {
                 mAppOpsService.removeUser(userId);
             } catch (RemoteException e) {
@@ -4600,9 +4637,11 @@
                             @Override
                             public void userStopped(int userIdParam) {
                                 finishRemoveUser(userIdParam);
+                                logUserRemoveJourneyFinish(sessionId, userIdParam, true);
                             }
                             @Override
                             public void userStopAborted(int userIdParam) {
+                                logUserRemoveJourneyFinish(sessionId, userIdParam, false);
                             }
                         });
             } catch (RemoteException e) {
@@ -5861,33 +5900,6 @@
         }
 
         @Override
-        public Bundle getBaseUserRestrictions(@UserIdInt int userId) {
-            synchronized (mRestrictionsLock) {
-                return mBaseUserRestrictions.getRestrictions(userId);
-            }
-        }
-
-        @Override
-        public void setBaseUserRestrictionsByDpmsForMigration(
-                @UserIdInt int userId, Bundle baseRestrictions) {
-            synchronized (mRestrictionsLock) {
-                if (mBaseUserRestrictions.updateRestrictions(userId,
-                        new Bundle(baseRestrictions))) {
-                    invalidateEffectiveUserRestrictionsLR(userId);
-                }
-            }
-
-            final UserData userData = getUserDataNoChecks(userId);
-            synchronized (mPackagesLock) {
-                if (userData != null) {
-                    writeUserLP(userData);
-                } else {
-                    Slog.w(LOG_TAG, "UserInfo not found for " + userId);
-                }
-            }
-        }
-
-        @Override
         public boolean getUserRestriction(@UserIdInt int userId, String key) {
             return getUserRestrictions(userId).getBoolean(key);
         }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index c524fb7..d11ea53 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -297,6 +297,8 @@
             .OnRuntimePermissionStateChangedListener>
             mRuntimePermissionStateChangedListeners = new ArrayList<>();
 
+    private final boolean mIsLeanback;
+
     @NonNull
     private final OnPermissionChangeListeners mOnPermissionChangeListeners;
 
@@ -380,6 +382,7 @@
         mContext = context;
         mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
         mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
+        mIsLeanback = availableFeatures.containsKey(PackageManager.FEATURE_LEANBACK);
 
         mPrivilegedPermissionAllowlistSourcePackageNames.add(PLATFORM_PACKAGE_NAME);
         // PackageManager.hasSystemFeature() is not used here because PackageManagerService
@@ -2822,6 +2825,14 @@
                                     }
                                 }
                             }
+                            if (mIsLeanback && NOTIFICATION_PERMISSIONS.contains(permName)) {
+                                uidState.grantPermission(bp);
+                                if (origPermState == null || !origPermState.isGranted()) {
+                                    if (uidState.grantPermission(bp)) {
+                                        wasChanged = true;
+                                    }
+                                }
+                            }
                         } else {
                             if (origPermState == null) {
                                 // New permission
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index aede4b1..685b744 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -64,6 +64,8 @@
 import com.android.server.statusbar.StatusBarManagerInternal;
 
 import java.io.PrintWriter;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Sends broadcasts about important power state changes.
@@ -133,6 +135,7 @@
     private final DisplayManagerInternal mDisplayManagerInternal;
 
     private final NotifierHandler mHandler;
+    private final Executor mBackgroundExecutor;
     private final Intent mScreenOnIntent;
     private final Intent mScreenOffIntent;
 
@@ -169,9 +172,12 @@
     // True if a user activity message should be sent.
     private boolean mUserActivityPending;
 
+    private final AtomicBoolean mIsPlayingChargingStartedFeedback = new AtomicBoolean(false);
+
     public Notifier(Looper looper, Context context, IBatteryStats batteryStats,
             SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
-            FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) {
+            FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector,
+            Executor backgroundExecutor) {
         mContext = context;
         mBatteryStats = batteryStats;
         mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -188,6 +194,7 @@
         mVibrator = mContext.getSystemService(Vibrator.class);
 
         mHandler = new NotifierHandler(looper);
+        mBackgroundExecutor = backgroundExecutor;
         mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
         mScreenOnIntent.addFlags(
                 Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND
@@ -824,25 +831,36 @@
             return;
         }
 
-        // vibrate
-        final boolean vibrate = Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0;
-        if (vibrate) {
-            mVibrator.vibrate(CHARGING_VIBRATION_EFFECT, HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
+        if (!mIsPlayingChargingStartedFeedback.compareAndSet(false, true)) {
+            // there's already a charging started feedback Runnable scheduled to run on the
+            // background thread, so let's not execute another
+            return;
         }
 
-        // play sound
-        final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
-                wireless ? Settings.Global.WIRELESS_CHARGING_STARTED_SOUND
-                        : Settings.Global.CHARGING_STARTED_SOUND);
-        final Uri soundUri = Uri.parse("file://" + soundPath);
-        if (soundUri != null) {
-            final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
-            if (sfx != null) {
-                sfx.setStreamType(AudioManager.STREAM_SYSTEM);
-                sfx.play();
+        // vibrate & play sound on a background thread
+        mBackgroundExecutor.execute(() -> {
+            // vibrate
+            final boolean vibrate = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                    Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0;
+            if (vibrate) {
+                mVibrator.vibrate(CHARGING_VIBRATION_EFFECT,
+                        HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
             }
-        }
+
+            // play sound
+            final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
+                    wireless ? Settings.Global.WIRELESS_CHARGING_STARTED_SOUND
+                            : Settings.Global.CHARGING_STARTED_SOUND);
+            final Uri soundUri = Uri.parse("file://" + soundPath);
+            if (soundUri != null) {
+                final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
+                if (sfx != null) {
+                    sfx.setStreamType(AudioManager.STREAM_SYSTEM);
+                    sfx.play();
+                }
+            }
+            mIsPlayingChargingStartedFeedback.set(false);
+        });
     }
 
     private void showWirelessChargingStarted(int batteryLevel, @UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 6e78ecb..e0da0e8 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -140,6 +140,7 @@
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Objects;
+import java.util.concurrent.Executor;
 
 /**
  * The power manager service is responsible for coordinating power management
@@ -905,10 +906,11 @@
     static class Injector {
         Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
                 SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
-                FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) {
+                FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector,
+                Executor backgroundExecutor) {
             return new Notifier(
                     looper, context, batteryStats, suspendBlocker, policy, faceDownDetector,
-                    screenUndimDetector);
+                    screenUndimDetector, backgroundExecutor);
         }
 
         SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
@@ -1227,7 +1229,8 @@
             mBatteryStats = BatteryStatsService.getService();
             mNotifier = mInjector.createNotifier(Looper.getMainLooper(), mContext, mBatteryStats,
                     mInjector.createSuspendBlocker(this, "PowerManagerService.Broadcasts"),
-                    mPolicy, mFaceDownDetector, mScreenUndimDetector);
+                    mPolicy, mFaceDownDetector, mScreenUndimDetector,
+                    BackgroundThread.getExecutor());
 
             mPowerGroups.append(Display.DEFAULT_DISPLAY_GROUP,
                     new PowerGroup(WAKEFULNESS_AWAKE, mPowerGroupWakefulnessChangeListener,
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index bce1cce..0834417 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -560,7 +560,7 @@
                         + " sensors");
                 return;
             }
-            mContext.startActivityAsUser(dialogIntent, options.toBundle(), info.mUser);
+            mContext.startActivityAsUser(dialogIntent, options.toBundle(), UserHandle.SYSTEM);
         }
 
         /**
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
index ae52912..7173f60 100644
--- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
@@ -59,6 +59,8 @@
      */
     private static final Set<String> CONFIGURATION_INTERNAL_SERVER_FLAGS_KEYS_TO_WATCH = Set.of(
             ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
+            ServerFlags.KEY_PRIMARY_LTZP_MODE_OVERRIDE,
+            ServerFlags.KEY_SECONDARY_LTZP_MODE_OVERRIDE,
             ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_RUN_IN_BACKGROUND_ENABLED,
             ServerFlags.KEY_ENHANCED_METRICS_COLLECTION_ENABLED,
             ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
@@ -443,6 +445,9 @@
         mTestPrimaryLocationTimeZoneProviderMode =
                 mTestPrimaryLocationTimeZoneProviderPackageName == null
                         ? PROVIDER_MODE_DISABLED : PROVIDER_MODE_ENABLED;
+        // Changing this state can affect the content of ConfigurationInternal, so listeners need to
+        // be informed.
+        mContext.getMainThreadHandler().post(this::handleConfigurationInternalChangeOnMainThread);
     }
 
     @Override
@@ -469,6 +474,9 @@
         mTestSecondaryLocationTimeZoneProviderMode =
                 mTestSecondaryLocationTimeZoneProviderPackageName == null
                         ? PROVIDER_MODE_DISABLED : PROVIDER_MODE_ENABLED;
+        // Changing this state can affect the content of ConfigurationInternal, so listeners need to
+        // be informed.
+        mContext.getMainThreadHandler().post(this::handleConfigurationInternalChangeOnMainThread);
     }
 
     @Override
@@ -573,6 +581,10 @@
         mTestSecondaryLocationTimeZoneProviderPackageName = null;
         mTestSecondaryLocationTimeZoneProviderMode = null;
         mRecordStateChangesForTests = false;
+
+        // Changing LTZP config can affect the content of ConfigurationInternal, so listeners
+        // need to be informed.
+        mContext.getMainThreadHandler().post(this::handleConfigurationInternalChangeOnMainThread);
     }
 
     private boolean isTelephonyFallbackSupported() {
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index f75608e..898d02e 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -336,13 +336,13 @@
     boolean isTelephonyTimeZoneDetectionSupported() {
         enforceManageTimeZoneDetectorPermission();
 
-        return mServiceConfigAccessor.isTelephonyTimeZoneDetectionFeatureSupported();
+        return mTimeZoneDetectorStrategy.isTelephonyTimeZoneDetectionSupported();
     }
 
     boolean isGeoTimeZoneDetectionSupported() {
         enforceManageTimeZoneDetectorPermission();
 
-        return mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported();
+        return mTimeZoneDetectorStrategy.isGeoTimeZoneDetectionSupported();
     }
 
     /**
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index 6b04adf..95ebd68 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -124,4 +124,10 @@
     /** Generates a state snapshot for metrics. */
     @NonNull
     MetricsTimeZoneDetectorState generateMetricsState();
+
+    /** Returns {@code true} if the device supports telephony time zone detection. */
+    boolean isTelephonyTimeZoneDetectionSupported();
+
+    /** Returns {@code true} if the device supports geolocation time zone detection. */
+    boolean isGeoTimeZoneDetectionSupported();
 }
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index e21d0e4..66c23f5 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -396,6 +396,20 @@
                 getLatestGeolocationSuggestion());
     }
 
+    @Override
+    public boolean isTelephonyTimeZoneDetectionSupported() {
+        synchronized (this) {
+            return mCurrentConfigurationInternal.isTelephonyDetectionSupported();
+        }
+    }
+
+    @Override
+    public boolean isGeoTimeZoneDetectionSupported() {
+        synchronized (this) {
+            return mCurrentConfigurationInternal.isGeoDetectionSupported();
+        }
+    }
+
     private static int scoreTelephonySuggestion(@NonNull TelephonyTimeZoneSuggestion suggestion) {
         int score;
         if (suggestion.getZoneId() == null) {
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index f29c40f..37f0450 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -341,6 +341,9 @@
                 if (gatewayConnection == null) {
                     logWtf("Found gatewayConnectionConfig without GatewayConnection");
                 } else {
+                    logInfo(
+                            "Config updated, restarting gateway "
+                                    + gatewayConnection.getLogPrefix());
                     gatewayConnection.teardownAsynchronously();
                 }
             }
@@ -397,7 +400,7 @@
         // If preexisting VcnGatewayConnection(s) satisfy request, return
         for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) {
             if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
-                logDbg("Request already satisfied by existing VcnGatewayConnection: " + request);
+                logVdbg("Request already satisfied by existing VcnGatewayConnection: " + request);
                 return;
             }
         }
@@ -407,8 +410,6 @@
         for (VcnGatewayConnectionConfig gatewayConnectionConfig :
                 mConfig.getGatewayConnectionConfigs()) {
             if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
-                logDbg("Bringing up new VcnGatewayConnection for request " + request);
-
                 if (getExposedCapabilitiesForMobileDataState(gatewayConnectionConfig).isEmpty()) {
                     // Skip; this network does not provide any services if mobile data is disabled.
                     continue;
@@ -424,6 +425,7 @@
                     return;
                 }
 
+                logInfo("Bringing up new VcnGatewayConnection for request " + request);
                 final VcnGatewayConnection vcnGatewayConnection =
                         mDeps.newVcnGatewayConnection(
                                 mVcnContext,
@@ -455,7 +457,7 @@
     }
 
     private void handleGatewayConnectionQuit(VcnGatewayConnectionConfig config) {
-        logDbg("VcnGatewayConnection quit: " + config);
+        logInfo("VcnGatewayConnection quit: " + config);
         mVcnGatewayConnections.remove(config);
 
         // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be satisfied
@@ -534,7 +536,7 @@
             // Trigger re-evaluation of all requests; mobile data state impacts supported caps.
             mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener);
 
-            logDbg("Mobile data " + (mIsMobileDataEnabled ? "enabled" : "disabled"));
+            logInfo("Mobile data " + (mIsMobileDataEnabled ? "enabled" : "disabled"));
         }
     }
 
@@ -569,11 +571,11 @@
     }
 
     private String getLogPrefix() {
-        return "["
+        return "("
                 + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup)
                 + "-"
                 + System.identityHashCode(this)
-                + "] ";
+                + ") ";
     }
 
     private void logVdbg(String msg) {
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index be38005..cefd8ef 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -732,14 +732,11 @@
         logDbg("Triggering async teardown");
         sendDisconnectRequestedAndAcquireWakelock(
                 DISCONNECT_REASON_TEARDOWN, true /* shouldQuit */);
-
-        // TODO: Notify VcnInstance (via callbacks) of permanent teardown of this tunnel, since this
-        // is also called asynchronously when a NetworkAgent becomes unwanted
     }
 
     @Override
     protected void onQuitting() {
-        logDbg("Quitting VcnGatewayConnection");
+        logInfo("Quitting VcnGatewayConnection");
 
         if (mNetworkAgent != null) {
             logWtf("NetworkAgent was non-null in onQuitting");
@@ -794,7 +791,7 @@
             // TODO(b/180132994): explore safely removing this Thread check
             mVcnContext.ensureRunningOnLooperThread();
 
-            logDbg(
+            logInfo(
                     "Selected underlying network changed: "
                             + (underlying == null ? null : underlying.network));
 
@@ -1335,7 +1332,7 @@
         protected void handleDisconnectRequested(EventDisconnectRequestedInfo info) {
             // TODO(b/180526152): notify VcnStatusCallback for Network loss
 
-            logDbg("Tearing down. Cause: " + info.reason);
+            logInfo("Tearing down. Cause: " + info.reason + "; quitting = " + info.shouldQuit);
             if (info.shouldQuit) {
                 mIsQuitting.setTrue();
             }
@@ -1353,7 +1350,7 @@
 
         protected void handleSafeModeTimeoutExceeded() {
             mSafeModeTimeoutAlarm = null;
-            logDbg("Entering safe mode after timeout exceeded");
+            logInfo("Entering safe mode after timeout exceeded");
 
             // Connectivity for this GatewayConnection is broken; tear down the Network.
             teardownNetwork();
@@ -1362,7 +1359,7 @@
         }
 
         protected void logUnexpectedEvent(int what) {
-            logDbg(
+            logVdbg(
                     "Unexpected event code "
                             + what
                             + " in state "
@@ -1672,7 +1669,7 @@
                                     return;
                                 }
 
-                                logDbg("NetworkAgent was unwanted");
+                                logInfo("NetworkAgent was unwanted");
                                 teardownAsynchronously();
                             } /* networkUnwantedCallback */,
                             (status) -> {
@@ -1748,7 +1745,7 @@
                             tunnelIface, IpSecManager.DIRECTION_FWD, transform);
                 }
             } catch (IOException e) {
-                logDbg("Transform application failed for network " + token, e);
+                logInfo("Transform application failed for network " + token, e);
                 sessionLost(token, e);
             }
         }
@@ -1782,7 +1779,7 @@
                     tunnelIface.removeAddress(address.getAddress(), address.getPrefixLength());
                 }
             } catch (IOException e) {
-                logDbg("Adding address to tunnel failed for token " + token, e);
+                logInfo("Adding address to tunnel failed for token " + token, e);
                 sessionLost(token, e);
             }
         }
@@ -1862,7 +1859,7 @@
         }
 
         private void handleMigrationCompleted(EventMigrationCompletedInfo migrationCompletedInfo) {
-            logDbg("Migration completed: " + mUnderlying.network);
+            logInfo("Migration completed: " + mUnderlying.network);
 
             applyTransform(
                     mCurrentToken,
@@ -1890,7 +1887,7 @@
             mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
 
             if (mUnderlying == null) {
-                logDbg("Underlying network lost");
+                logInfo("Underlying network lost");
 
                 // Ignored for now; a new network may be coming up. If none does, the delayed
                 // NETWORK_LOST disconnect will be fired, and tear down the session + network.
@@ -1900,7 +1897,7 @@
             // mUnderlying assumed non-null, given check above.
             // If network changed, migrate. Otherwise, update any existing networkAgent.
             if (oldUnderlying == null || !oldUnderlying.network.equals(mUnderlying.network)) {
-                logDbg("Migrating to new network: " + mUnderlying.network);
+                logInfo("Migrating to new network: " + mUnderlying.network);
                 mIkeSession.setNetwork(mUnderlying.network);
             } else {
                 // oldUnderlying is non-null & underlying network itself has not changed
@@ -2168,13 +2165,13 @@
 
         @Override
         public void onClosedExceptionally(@NonNull IkeException exception) {
-            logDbg("IkeClosedExceptionally for token " + mToken, exception);
+            logInfo("IkeClosedExceptionally for token " + mToken, exception);
             sessionClosed(mToken, exception);
         }
 
         @Override
         public void onError(@NonNull IkeProtocolException exception) {
-            logDbg("IkeError for token " + mToken, exception);
+            logInfo("IkeError for token " + mToken, exception);
             // Non-fatal, log and continue.
         }
     }
@@ -2208,7 +2205,7 @@
 
         @Override
         public void onClosedExceptionally(@NonNull IkeException exception) {
-            logDbg("ChildClosedExceptionally for token " + mToken, exception);
+            logInfo("ChildClosedExceptionally for token " + mToken, exception);
             sessionLost(mToken, exception);
         }
 
@@ -2234,14 +2231,19 @@
         }
     }
 
-    private String getLogPrefix() {
-        return "["
+    // Used in Vcn.java, but must be public for mockito to mock this.
+    public String getLogPrefix() {
+        return "("
                 + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup)
                 + "-"
                 + mConnectionConfig.getGatewayConnectionName()
                 + "-"
                 + System.identityHashCode(this)
-                + "] ";
+                + ") ";
+    }
+
+    private String getTagLogPrefix() {
+        return "[ " + TAG + " " + getLogPrefix() + "]";
     }
 
     private void logVdbg(String msg) {
@@ -2258,34 +2260,44 @@
         Slog.d(TAG, getLogPrefix() + msg, tr);
     }
 
+    private void logInfo(String msg) {
+        Slog.i(TAG, getLogPrefix() + msg);
+        LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg);
+    }
+
+    private void logInfo(String msg, Throwable tr) {
+        Slog.i(TAG, getLogPrefix() + msg, tr);
+        LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg + tr);
+    }
+
     private void logWarn(String msg) {
         Slog.w(TAG, getLogPrefix() + msg);
-        LOCAL_LOG.log(getLogPrefix() + "WARN: " + msg);
+        LOCAL_LOG.log("[WARN] " + getTagLogPrefix() + msg);
     }
 
     private void logWarn(String msg, Throwable tr) {
         Slog.w(TAG, getLogPrefix() + msg, tr);
-        LOCAL_LOG.log(getLogPrefix() + "WARN: " + msg + tr);
+        LOCAL_LOG.log("[WARN] " + getTagLogPrefix() + msg + tr);
     }
 
     private void logErr(String msg) {
         Slog.e(TAG, getLogPrefix() + msg);
-        LOCAL_LOG.log(getLogPrefix() + "ERR: " + msg);
+        LOCAL_LOG.log("[ERR ] " + getTagLogPrefix() + msg);
     }
 
     private void logErr(String msg, Throwable tr) {
         Slog.e(TAG, getLogPrefix() + msg, tr);
-        LOCAL_LOG.log(getLogPrefix() + "ERR: " + msg + tr);
+        LOCAL_LOG.log("[ERR ] " + getTagLogPrefix() + msg + tr);
     }
 
     private void logWtf(String msg) {
         Slog.wtf(TAG, getLogPrefix() + msg);
-        LOCAL_LOG.log(getLogPrefix() + "WTF: " + msg);
+        LOCAL_LOG.log("[WTF ] " + msg);
     }
 
     private void logWtf(String msg, Throwable tr) {
         Slog.wtf(TAG, getLogPrefix() + msg, tr);
-        LOCAL_LOG.log(getLogPrefix() + "WTF: " + msg + tr);
+        LOCAL_LOG.log("[WTF ] " + msg + tr);
     }
 
     /**
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index ca2e449..a3babf7 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -48,6 +48,7 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import com.android.server.vcn.VcnContext;
+import com.android.server.vcn.util.LogUtils;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -368,6 +369,18 @@
             return;
         }
 
+        String allNetworkPriorities = "";
+        for (UnderlyingNetworkRecord record : sorted) {
+            if (!allNetworkPriorities.isEmpty()) {
+                allNetworkPriorities += ", ";
+            }
+            allNetworkPriorities += record.network + ": " + record.getPriorityClass();
+        }
+        logInfo(
+                "Selected network changed to "
+                        + (candidate == null ? null : candidate.network)
+                        + ", selected from list: "
+                        + allNetworkPriorities);
         mCurrentRecord = candidate;
         mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord);
     }
@@ -478,14 +491,38 @@
         }
     }
 
-    private static void logWtf(String msg) {
-        Slog.wtf(TAG, msg);
-        LOCAL_LOG.log(TAG + " WTF: " + msg);
+    private String getLogPrefix() {
+        return "("
+                + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup)
+                + "-"
+                + mConnectionConfig.getGatewayConnectionName()
+                + "-"
+                + System.identityHashCode(this)
+                + ") ";
     }
 
-    private static void logWtf(String msg, Throwable tr) {
+    private String getTagLogPrefix() {
+        return "[ " + TAG + " " + getLogPrefix() + "]";
+    }
+
+    private void logInfo(String msg) {
+        Slog.i(TAG, getLogPrefix() + msg);
+        LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg);
+    }
+
+    private void logInfo(String msg, Throwable tr) {
+        Slog.i(TAG, getLogPrefix() + msg, tr);
+        LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg + tr);
+    }
+
+    private void logWtf(String msg) {
+        Slog.wtf(TAG, msg);
+        LOCAL_LOG.log(TAG + "[WTF ] " + getTagLogPrefix() + msg);
+    }
+
+    private void logWtf(String msg, Throwable tr) {
         Slog.wtf(TAG, msg, tr);
-        LOCAL_LOG.log(TAG + " WTF: " + msg + tr);
+        LOCAL_LOG.log(TAG + "[WTF ] " + getTagLogPrefix() + msg + tr);
     }
 
     /** Dumps the state of this record for logging and debugging purposes. */
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
index c0488b1..06f9280 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
@@ -41,11 +41,15 @@
  * @hide
  */
 public class UnderlyingNetworkRecord {
+    private static final int PRIORITY_CLASS_INVALID = Integer.MAX_VALUE;
+
     @NonNull public final Network network;
     @NonNull public final NetworkCapabilities networkCapabilities;
     @NonNull public final LinkProperties linkProperties;
     public final boolean isBlocked;
 
+    private int mPriorityClass = PRIORITY_CLASS_INVALID;
+
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     public UnderlyingNetworkRecord(
             @NonNull Network network,
@@ -58,6 +62,34 @@
         this.isBlocked = isBlocked;
     }
 
+    private int getOrCalculatePriorityClass(
+            VcnContext vcnContext,
+            List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+            ParcelUuid subscriptionGroup,
+            TelephonySubscriptionSnapshot snapshot,
+            UnderlyingNetworkRecord currentlySelected,
+            PersistableBundle carrierConfig) {
+        // Never changes after the underlying network record is created.
+        if (mPriorityClass == PRIORITY_CLASS_INVALID) {
+            mPriorityClass =
+                    NetworkPriorityClassifier.calculatePriorityClass(
+                            vcnContext,
+                            this,
+                            underlyingNetworkTemplates,
+                            subscriptionGroup,
+                            snapshot,
+                            currentlySelected,
+                            carrierConfig);
+        }
+
+        return mPriorityClass;
+    }
+
+    // Used in UnderlyingNetworkController
+    int getPriorityClass() {
+        return mPriorityClass;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -84,18 +116,16 @@
             PersistableBundle carrierConfig) {
         return (left, right) -> {
             final int leftIndex =
-                    NetworkPriorityClassifier.calculatePriorityClass(
+                    left.getOrCalculatePriorityClass(
                             vcnContext,
-                            left,
                             underlyingNetworkTemplates,
                             subscriptionGroup,
                             snapshot,
                             currentlySelected,
                             carrierConfig);
             final int rightIndex =
-                    NetworkPriorityClassifier.calculatePriorityClass(
+                    right.getOrCalculatePriorityClass(
                             vcnContext,
-                            right,
                             underlyingNetworkTemplates,
                             subscriptionGroup,
                             snapshot,
@@ -142,16 +172,15 @@
         pw.increaseIndent();
 
         final int priorityIndex =
-                NetworkPriorityClassifier.calculatePriorityClass(
+                getOrCalculatePriorityClass(
                         vcnContext,
-                        this,
                         underlyingNetworkTemplates,
                         subscriptionGroup,
                         snapshot,
                         currentlySelected,
                         carrierConfig);
 
-        pw.println("Priority index:" + priorityIndex);
+        pw.println("Priority index: " + priorityIndex);
         pw.println("mNetwork: " + network);
         pw.println("mNetworkCapabilities: " + networkCapabilities);
         pw.println("mLinkProperties: " + linkProperties);
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 18e9904..ac635a0 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -238,8 +238,6 @@
         // Listen to all settings that might affect the result of Vibrator.getVibrationIntensity.
         registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES));
         registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_ON));
-        registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING));
-        registerSettingsObserver(Settings.System.getUriFor(Settings.System.APPLY_RAMPING_RINGER));
         registerSettingsObserver(Settings.System.getUriFor(
                 Settings.System.HAPTIC_FEEDBACK_ENABLED));
         registerSettingsObserver(
@@ -449,19 +447,12 @@
             mCurrentVibrationIntensities.put(USAGE_NOTIFICATION, notificationIntensity);
             mCurrentVibrationIntensities.put(USAGE_MEDIA, mediaIntensity);
             mCurrentVibrationIntensities.put(USAGE_UNKNOWN, mediaIntensity);
+            mCurrentVibrationIntensities.put(USAGE_RINGTONE, ringIntensity);
 
             // Communication request is not disabled by the notification setting.
             mCurrentVibrationIntensities.put(USAGE_COMMUNICATION_REQUEST,
                     positiveNotificationIntensity);
 
-            if (!loadBooleanSetting(Settings.System.VIBRATE_WHEN_RINGING)
-                    && !loadBooleanSetting(Settings.System.APPLY_RAMPING_RINGER)) {
-                // Make sure deprecated boolean setting still disables ringtone vibrations.
-                mCurrentVibrationIntensities.put(USAGE_RINGTONE, Vibrator.VIBRATION_INTENSITY_OFF);
-            } else {
-                mCurrentVibrationIntensities.put(USAGE_RINGTONE, ringIntensity);
-            }
-
             // This should adapt the behavior preceding the introduction of this new setting
             // key, which is to apply HAPTIC_FEEDBACK_INTENSITY, unless it's disabled.
             mCurrentVibrationIntensities.put(USAGE_HARDWARE_FEEDBACK, hardwareFeedbackIntensity);
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index c52a4e4..63619e5 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -446,20 +446,6 @@
         // Not relevant for the window observer.
     }
 
-    MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
-        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
-            mAccessibilityTracing.logTrace(TAG + ".getMagnificationSpecForWindow",
-                    FLAGS_MAGNIFICATION_CALLBACK,
-                    "windowState={" + windowState + "}");
-        }
-        final int displayId = windowState.getDisplayId();
-        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
-        if (displayMagnifier != null) {
-            return displayMagnifier.getMagnificationSpecForWindow(windowState);
-        }
-        return null;
-    }
-
     boolean hasCallbacks() {
         if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
                 | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
index ca636b3..6e5011d 100644
--- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
+++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
@@ -596,8 +596,6 @@
      * surface flinger to the accessibility framework.
      */
     public static class AccessibilityWindow {
-        private static final Region TEMP_REGION = new Region();
-        private static final RectF TEMP_RECTF = new RectF();
         // Data
         private IWindow mWindow;
         private int mDisplayId;
@@ -615,15 +613,16 @@
         private final Region mLetterBoxBounds = new Region();
         private WindowInfo mWindowInfo;
 
+
         /**
          * Returns the instance after initializing the internal data.
          * @param service The window manager service.
          * @param inputWindowHandle The window from the surface flinger.
-         * @param inverseMatrix The magnification spec inverse matrix.
+         * @param magnificationInverseMatrix The magnification spec inverse matrix.
          */
         public static AccessibilityWindow initializeData(WindowManagerService service,
-                InputWindowHandle inputWindowHandle, Matrix inverseMatrix, IBinder pipIBinder,
-                Matrix displayMatrix) {
+                InputWindowHandle inputWindowHandle, Matrix magnificationInverseMatrix,
+                IBinder pipIBinder, Matrix displayMatrix) {
             final IWindow window = inputWindowHandle.getWindow();
             final WindowState windowState = window != null ? service.mWindowMap.get(
                     window.asBinder()) : null;
@@ -655,13 +654,35 @@
                     inputWindowHandle.frameTop, inputWindowHandle.frameRight,
                     inputWindowHandle.frameBottom);
             getTouchableRegionInWindow(instance.mShouldMagnify, inputWindowHandle.touchableRegion,
-                    instance.mTouchableRegionInWindow, windowFrame, inverseMatrix, displayMatrix);
+                    instance.mTouchableRegionInWindow, windowFrame, magnificationInverseMatrix,
+                    displayMatrix);
             getUnMagnifiedTouchableRegion(instance.mShouldMagnify,
                     inputWindowHandle.touchableRegion, instance.mTouchableRegionInScreen,
-                    inverseMatrix, displayMatrix);
+                    magnificationInverseMatrix, displayMatrix);
             instance.mWindowInfo = windowState != null
                     ? windowState.getWindowInfo() : getWindowInfoForWindowlessWindows(instance);
 
+            // Compute the transform matrix that will transform bounds from the window
+            // coordinates to screen coordinates.
+            final Matrix inverseTransform = new Matrix();
+            inputWindowHandle.transform.invert(inverseTransform);
+            inverseTransform.postConcat(displayMatrix);
+            inverseTransform.getValues(instance.mWindowInfo.mTransformMatrix);
+
+            // Compute the magnification spec matrix.
+            final Matrix magnificationSpecMatrix = new Matrix();
+            if (instance.shouldMagnify() && magnificationInverseMatrix != null
+                    && !magnificationInverseMatrix.isIdentity()) {
+                if (magnificationInverseMatrix.invert(magnificationSpecMatrix)) {
+                    magnificationSpecMatrix.getValues(sTempFloats);
+                    final MagnificationSpec spec = instance.mWindowInfo.mMagnificationSpec;
+                    spec.scale = sTempFloats[Matrix.MSCALE_X];
+                    spec.offsetX = sTempFloats[Matrix.MTRANS_X];
+                    spec.offsetY = sTempFloats[Matrix.MTRANS_Y];
+                } else {
+                    Slog.w(TAG, "can't find spec");
+                }
+            }
             return instance;
         }
 
@@ -779,7 +800,7 @@
             // for the consistency and match developers expectation.
             // So we need to make the intersection between the frame and touchable region to
             // obtain the real touch region in the screen.
-            Region touchRegion = TEMP_REGION;
+            Region touchRegion = new Region();
             touchRegion.set(inRegion);
             touchRegion.op(frame, Region.Op.INTERSECT);
 
@@ -807,8 +828,7 @@
 
             forEachRect(inRegion, rect -> {
                 // Move to origin as all transforms are captured by the matrix.
-                RectF windowFrame = TEMP_RECTF;
-                windowFrame.set(rect);
+                RectF windowFrame = new RectF(rect);
 
                 displayMatrix.mapRect(windowFrame);
                 inverseMatrix.mapRect(windowFrame);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c525129..dc4e117 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -6300,7 +6300,7 @@
         // starting window is drawn, the transition can start earlier. Exclude finishing and bubble
         // because it may be a trampoline.
         if (!wasTaskVisible && mStartingData != null && !finishing && !mLaunchedFromBubble
-                && !mDisplayContent.mAppTransition.isReady()
+                && mVisibleRequested && !mDisplayContent.mAppTransition.isReady()
                 && !mDisplayContent.mAppTransition.isRunning()
                 && mDisplayContent.isNextTransitionForward()) {
             // The pending transition state will be cleared after the transition is started, so
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index eb912d4..75d4621 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2481,6 +2481,12 @@
             if (inTaskFragment == null) {
                 inTaskFragment = TaskFragment.fromTaskFragmentToken(
                         mOptions.getLaunchTaskFragmentToken(), mService);
+                if (inTaskFragment != null && inTaskFragment.isEmbeddedTaskFragmentInPip()) {
+                    // Do not start activity in TaskFragment in a PIP Task.
+                    Slog.w(TAG, "Can not start activity in TaskFragment in PIP: "
+                            + inTaskFragment);
+                    inTaskFragment = null;
+                }
             }
         }
 
@@ -2638,14 +2644,16 @@
             Slog.w(TAG, "startActivity called from finishing " + mSourceRecord
                     + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
             mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
-            mNewTaskInfo = mSourceRecord.info;
 
-            // It is not guaranteed that the source record will have a task associated with it. For,
-            // example, if this method is being called for processing a pending activity launch, it
-            // is possible that the activity has been removed from the task after the launch was
-            // enqueued.
+            // It is not guaranteed that the source record will have a task associated with it.
+            // For example, if this method is being called for processing a pending activity
+            // launch, it is possible that the activity has been removed from the task after the
+            // launch was enqueued.
             final Task sourceTask = mSourceRecord.getTask();
-            mNewTaskIntent = sourceTask != null ? sourceTask.intent : null;
+            if (sourceTask == null || sourceTask.getTopNonFinishingActivity() == null) {
+                mNewTaskInfo = mSourceRecord.info;
+                mNewTaskIntent = sourceTask != null ? sourceTask.intent : null;
+            }
         }
         mSourceRecord = null;
         mSourceRootTask = null;
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index f70dc52..0ed6718 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -94,12 +94,22 @@
         }
 
         int backType = BackNavigationInfo.TYPE_UNDEFINED;
+
+        // The currently visible activity (if any).
+        ActivityRecord currentActivity = null;
+
+        // The currently visible task (if any).
+        Task currentTask = null;
+
+        // The previous task we're going back to. Can be the same as currentTask, if there are
+        // multiple Activities in the Stack.
         Task prevTask = null;
-        ActivityRecord prev;
+
+        // The previous activity we're going back to. This can be either a child of currentTask
+        // if there are more than one Activity in currentTask, or a child of prevTask, if
+        // currentActivity is the last child of currentTask.
+        ActivityRecord prevActivity;
         WindowContainer<?> removedWindowContainer = null;
-        ActivityRecord activityRecord = null;
-        ActivityRecord prevTaskTopActivity = null;
-        Task task = null;
         SurfaceControl animationLeashParent = null;
         HardwareBuffer screenshotBuffer = null;
         RemoteAnimationTarget topAppTarget = null;
@@ -143,20 +153,37 @@
             }
 
             if (window == null) {
-                // We don't have any focused window, fallback ont the top task of the focused
+                // We don't have any focused window, fallback ont the top currentTask of the focused
                 // display.
                 ProtoLog.w(WM_DEBUG_BACK_PREVIEW,
-                        "No focused window, defaulting to top task's window");
-                task = wmService.mAtmService.getTopDisplayFocusedRootTask();
-                window = task.getWindow(WindowState::isFocused);
+                        "No focused window, defaulting to top current task's window");
+                currentTask = wmService.mAtmService.getTopDisplayFocusedRootTask();
+                window = currentTask.getWindow(WindowState::isFocused);
             }
 
             // Now let's find if this window has a callback from the client side.
             OnBackInvokedCallbackInfo callbackInfo = null;
             if (window != null) {
-                activityRecord = window.mActivityRecord;
-                task = window.getTask();
+                currentActivity = window.mActivityRecord;
+                currentTask = window.getTask();
                 callbackInfo = window.getOnBackInvokedCallbackInfo();
+                final DisplayContent displayContent = window.getDisplayContent();
+
+                // When IME is shown, return the more prioritized callback between IME and app.
+                // Priority ordering follows: OVERLAY, IME, DEFAULT.
+                if (displayContent != null && displayContent.getImeContainer().isVisible()) {
+                    WindowState imeWindow = displayContent.getImeContainer().getWindow(
+                            windowState -> windowState.getOnBackInvokedCallbackInfo() != null);
+                    if (imeWindow != null) {
+                        OnBackInvokedCallbackInfo imeCallbackInfo =
+                                imeWindow.getOnBackInvokedCallbackInfo();
+                        if (imeCallbackInfo != null && (callbackInfo == null
+                                || callbackInfo.getPriority() <= imeCallbackInfo.getPriority())) {
+                            callbackInfo = imeCallbackInfo;
+                        }
+                    }
+                }
+
                 if (callbackInfo == null) {
                     Slog.e(TAG, "No callback registered, returning null.");
                     return null;
@@ -167,9 +194,9 @@
                 infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback());
             }
 
-            ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation task=%s, "
+            ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation currentTask=%s, "
                             + "topRunningActivity=%s, callbackInfo=%s, currentFocus=%s",
-                    task, activityRecord, callbackInfo, window);
+                    currentTask, currentActivity, callbackInfo, window);
 
             if (window == null) {
                 Slog.e(TAG, "Window is null, returning null.");
@@ -179,21 +206,19 @@
             // If we don't need to set up the animation, we return early. This is the case when
             // - We have an application callback.
             // - We don't have any ActivityRecord or Task to animate.
-            // - The IME is opened, and we just need to close it.
             // - The home activity is the focused activity.
             if (backType == BackNavigationInfo.TYPE_CALLBACK
-                    || activityRecord == null
-                    || task == null
-                    || task.getDisplayContent().getImeContainer().isVisible()
-                    || activityRecord.isActivityTypeHome()) {
+                    || currentActivity == null
+                    || currentTask == null
+                    || currentActivity.isActivityTypeHome()) {
                 return infoBuilder
                         .setType(backType)
                         .build();
             }
 
             // We don't have an application callback, let's find the destination of the back gesture
-            Task finalTask = task;
-            prev = task.getActivity(
+            Task finalTask = currentTask;
+            prevActivity = currentTask.getActivity(
                     (r) -> !r.finishing && r.getTask() == finalTask && !r.isTopRunningActivity());
             if (window.getParent().getChildCount() > 1 && window.getParent().getChildAt(0)
                     != window) {
@@ -201,24 +226,24 @@
                 // activity, we won't close the activity.
                 backType = BackNavigationInfo.TYPE_DIALOG_CLOSE;
                 removedWindowContainer = window;
-            } else if (prev != null) {
-                // We have another Activity in the same task to go to
+            } else if (prevActivity != null) {
+                // We have another Activity in the same currentTask to go to
                 backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY;
-                removedWindowContainer = activityRecord;
-            } else if (task.returnsToHomeRootTask()) {
+                removedWindowContainer = currentActivity;
+            } else if (currentTask.returnsToHomeRootTask()) {
                 // Our Task should bring back to home
-                removedWindowContainer = task;
+                removedWindowContainer = currentTask;
                 backType = BackNavigationInfo.TYPE_RETURN_TO_HOME;
-            } else if (activityRecord.isRootOfTask()) {
+            } else if (currentActivity.isRootOfTask()) {
                 // TODO(208789724): Create single source of truth for this, maybe in
                 //  RootWindowContainer
-                // TODO: Also check Task.shouldUpRecreateTaskLocked() for prev logic
-                prevTask = task.mRootWindowContainer.getTaskBelow(task);
-                removedWindowContainer = task;
+                // TODO: Also check Task.shouldUpRecreateTaskLocked() for prevActivity logic
+                prevTask = currentTask.mRootWindowContainer.getTaskBelow(currentTask);
+                removedWindowContainer = currentTask;
+                prevActivity = prevTask.getTopNonFinishingActivity();
                 if (prevTask.isActivityTypeHome()) {
                     backType = BackNavigationInfo.TYPE_RETURN_TO_HOME;
                 } else {
-                    prev = prevTask.getTopNonFinishingActivity();
                     backType = BackNavigationInfo.TYPE_CROSS_TASK;
                 }
             }
@@ -229,7 +254,7 @@
 
             ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Previous Destination is Activity:%s Task:%s "
                             + "removedContainer:%s, backType=%s",
-                    prev != null ? prev.mActivityComponent : null,
+                    prevActivity != null ? prevActivity.mActivityComponent : null,
                     prevTask != null ? prevTask.getName() : null,
                     removedWindowContainer,
                     BackNavigationInfo.typeToString(backType));
@@ -241,7 +266,8 @@
                     && !removedWindowContainer.hasCommittedReparentToAnimationLeash();
 
             if (prepareAnimation) {
-                taskWindowConfiguration = task.getTaskInfo().configuration.windowConfiguration;
+                taskWindowConfiguration =
+                        currentTask.getTaskInfo().configuration.windowConfiguration;
 
                 infoBuilder.setTaskWindowConfiguration(taskWindowConfiguration);
                 // Prepare a leash to animate the current top window
@@ -254,32 +280,36 @@
                 removedWindowContainer.reparentSurfaceControl(tx, animLeash);
                 animationLeashParent = removedWindowContainer.getAnimationLeashParent();
                 topAppTarget = createRemoteAnimationTargetLocked(removedWindowContainer,
-                        activityRecord,
-                        task, animLeash);
+                        currentActivity,
+                        currentTask, animLeash);
                 infoBuilder.setDepartingAnimationTarget(topAppTarget);
             }
 
             //TODO(207481538) Remove once the infrastructure to support per-activity screenshot is
             // implemented. For now we simply have the mBackScreenshots hash map that dumbly
             // saves the screenshots.
-            if (needsScreenshot(backType) && prev != null && prev.mActivityComponent != null) {
-                screenshotBuffer = getActivitySnapshot(task, prev.mActivityComponent);
+            if (needsScreenshot(backType) && prevActivity != null
+                    && prevActivity.mActivityComponent != null) {
+                screenshotBuffer =
+                        getActivitySnapshot(currentTask, prevActivity.mActivityComponent);
             }
 
-            if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && isAnimationEnabled()) {
-                task.mBackGestureStarted = true;
+            // Special handling for back to home animation
+            if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && isAnimationEnabled()
+                    && prevTask != null) {
+                currentTask.mBackGestureStarted = true;
                 // Make launcher show from behind by marking its top activity as visible and
                 // launch-behind to bump its visibility for the duration of the back gesture.
-                prevTaskTopActivity = prevTask.getTopNonFinishingActivity();
-                if (prevTaskTopActivity != null) {
-                    if (!prevTaskTopActivity.mVisibleRequested) {
-                        prevTaskTopActivity.setVisibility(true);
+                prevActivity = prevTask.getTopNonFinishingActivity();
+                if (prevActivity != null) {
+                    if (!prevActivity.mVisibleRequested) {
+                        prevActivity.setVisibility(true);
                     }
-                    prevTaskTopActivity.mLaunchTaskBehind = true;
+                    prevActivity.mLaunchTaskBehind = true;
                     ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
                             "Setting Activity.mLauncherTaskBehind to true. Activity=%s",
-                            prevTaskTopActivity);
-                    prevTaskTopActivity.mRootWindowContainer.ensureActivitiesVisible(
+                            prevActivity);
+                    prevActivity.mRootWindowContainer.ensureActivitiesVisible(
                             null /* starting */, 0 /* configChanges */,
                             false /* preserveWindows */);
                 }
@@ -290,7 +320,7 @@
         if (topAppTarget != null && needsScreenshot(backType) && prevTask != null
                 && screenshotBuffer == null) {
             SurfaceControl.Builder builder = new SurfaceControl.Builder()
-                    .setName("BackPreview Screenshot for " + prev)
+                    .setName("BackPreview Screenshot for " + prevActivity)
                     .setParent(animationLeashParent)
                     .setHidden(false)
                     .setBLASTLayer();
@@ -302,12 +332,12 @@
             // The Animation leash needs to be above the screenshot surface, but the animation leash
             // needs to be added before to be in the synchronized block.
             tx.setLayer(topAppTarget.leash, 1);
-            tx.apply();
+        }
 
-
-            WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer;
+        WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer;
+        if (finalRemovedWindowContainer != null) {
             try {
-                activityRecord.token.linkToDeath(
+                currentActivity.token.linkToDeath(
                         () -> resetSurfaces(finalRemovedWindowContainer), 0);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to link to death", e);
@@ -315,11 +345,16 @@
                 return null;
             }
 
-            RemoteCallback onBackNavigationDone = new RemoteCallback(
-                    result -> resetSurfaces(finalRemovedWindowContainer
-                    ));
+            int finalBackType = backType;
+            ActivityRecord finalprevActivity = prevActivity;
+            Task finalTask = currentTask;
+            RemoteCallback onBackNavigationDone = new RemoteCallback(result -> onBackNavigationDone(
+                    result, finalRemovedWindowContainer, finalBackType, finalTask,
+                    finalprevActivity));
             infoBuilder.setOnBackNavigationDone(onBackNavigationDone);
         }
+
+        tx.apply();
         return infoBuilder.build();
     }
 
@@ -348,14 +383,13 @@
     }
 
     private void onBackNavigationDone(
-            Bundle result, WindowContainer windowContainer, int backType,
-            Task task, ActivityRecord prevTaskTopActivity) {
+            Bundle result, WindowContainer<?> windowContainer, int backType,
+            Task task, ActivityRecord prevActivity) {
         SurfaceControl surfaceControl = windowContainer.getSurfaceControl();
-        boolean triggerBack = result != null
-                ? result.getBoolean(BackNavigationInfo.KEY_TRIGGER_BACK)
-                : false;
+        boolean triggerBack = result != null && result.getBoolean(
+                BackNavigationInfo.KEY_TRIGGER_BACK);
         ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, "
-                + "task=%s, prevTaskTopActivity=%s", backType, task, prevTaskTopActivity);
+                + "task=%s, prevActivity=%s", backType, task, prevActivity);
 
         if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && isAnimationEnabled()) {
             if (triggerBack) {
@@ -367,13 +401,13 @@
                     t.apply();
                 }
             }
-            if (prevTaskTopActivity != null && !triggerBack) {
+            if (prevActivity != null && !triggerBack) {
                 // Restore the launch-behind state.
-                task.mTaskSupervisor.scheduleLaunchTaskBehindComplete(prevTaskTopActivity.token);
-                prevTaskTopActivity.mLaunchTaskBehind = false;
+                task.mTaskSupervisor.scheduleLaunchTaskBehindComplete(prevActivity.token);
+                prevActivity.mLaunchTaskBehind = false;
                 ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
                         "Setting Activity.mLauncherTaskBehind to false. Activity=%s",
-                        prevTaskTopActivity);
+                        prevActivity);
             }
         } else {
             task.mBackGestureStarted = false;
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 08a9da4..dbc08cd 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -190,6 +190,14 @@
      * @see #mFullConfiguration
      */
     public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
+        updateRequestedOverrideConfiguration(overrideConfiguration);
+        // Update full configuration of this container and all its children.
+        final ConfigurationContainer parent = getParent();
+        onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY);
+    }
+
+    /** Updates override configuration without recalculate full config. */
+    void updateRequestedOverrideConfiguration(Configuration overrideConfiguration) {
         // Pre-compute this here, so we don't need to go through the entire Configuration when
         // writing to proto (which has significant cost if we write a lot of empty configurations).
         mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration);
@@ -199,9 +207,6 @@
                 && diffRequestedOverrideMaxBounds(newBounds) != BOUNDS_CHANGE_NONE) {
             mRequestedOverrideConfiguration.windowConfiguration.setMaxBounds(newBounds);
         }
-        // Update full configuration of this container and all its children.
-        final ConfigurationContainer parent = getParent();
-        onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 566ed60..eaf82b6 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1146,8 +1146,13 @@
                 mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win,
                         (displayFrames, windowContainer, inOutFrame) -> {
                             if (!mNavButtonForcedVisible) {
-                                inOutFrame.inset(win.getLayoutingAttrs(
-                                        displayFrames.mRotation).providedInternalInsets);
+                                final Insets[] providedInternalInsets = win.getLayoutingAttrs(
+                                        displayFrames.mRotation).providedInternalInsets;
+                                if (providedInternalInsets != null
+                                        && providedInternalInsets.length > ITYPE_NAVIGATION_BAR
+                                        && providedInternalInsets[ITYPE_NAVIGATION_BAR] != null) {
+                                    inOutFrame.inset(providedInternalInsets[ITYPE_NAVIGATION_BAR]);
+                                }
                                 inOutFrame.inset(win.mGivenContentInsets);
                             }
                         },
@@ -1193,13 +1198,16 @@
                 if (attrs.providesInsetsTypes != null) {
                     for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) {
                         final TriConsumer<DisplayFrames, WindowContainer, Rect> imeFrameProvider =
-                                !attrs.providedInternalImeInsets.equals(Insets.NONE)
-                                        ? (displayFrames, windowContainer, inOutFrame) -> {
-                                            inOutFrame.inset(win.getLayoutingAttrs(
-                                                    displayFrames.mRotation)
-                                                    .providedInternalImeInsets);
-                                        }
-                                        : null;
+                                (displayFrames, windowContainer, inOutFrame) -> {
+                                    final Insets[] providedInternalImeInsets =
+                                            win.getLayoutingAttrs(displayFrames.mRotation)
+                                                    .providedInternalImeInsets;
+                                    if (providedInternalImeInsets != null
+                                            && providedInternalImeInsets.length > insetsType
+                                            && providedInternalImeInsets[insetsType] != null) {
+                                        inOutFrame.inset(providedInternalImeInsets[insetsType]);
+                                    }
+                                };
                         switch (insetsType) {
                             case ITYPE_STATUS_BAR:
                                 mStatusBarAlt = win;
@@ -1220,8 +1228,13 @@
                         }
                         mDisplayContent.setInsetProvider(insetsType, win, (displayFrames,
                                 windowContainer, inOutFrame) -> {
-                            inOutFrame.inset(win.getLayoutingAttrs(
-                                    displayFrames.mRotation).providedInternalInsets);
+                            final Insets[] providedInternalInsets = win.getLayoutingAttrs(
+                                    displayFrames.mRotation).providedInternalInsets;
+                            if (providedInternalInsets != null
+                                    && providedInternalInsets.length > insetsType
+                                    && providedInternalInsets[insetsType] != null) {
+                                inOutFrame.inset(providedInternalInsets[insetsType]);
+                            }
                             inOutFrame.inset(win.mGivenContentInsets);
                         }, imeFrameProvider);
                         mInsetsSourceWindowsExceptIme.add(win);
@@ -1937,15 +1950,23 @@
                 && lp.paramsForRotation[rotation] != null) {
             lp = lp.paramsForRotation[rotation];
         }
+        final Insets providedInternalInsets;
+        if (lp.providedInternalInsets != null
+                && lp.providedInternalInsets.length > ITYPE_NAVIGATION_BAR
+                && lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] != null) {
+            providedInternalInsets = lp.providedInternalInsets[ITYPE_NAVIGATION_BAR];
+        } else {
+            providedInternalInsets = Insets.NONE;
+        }
         if (position == NAV_BAR_LEFT) {
-            if (lp.width > lp.providedInternalInsets.right) {
-                return lp.width - lp.providedInternalInsets.right;
+            if (lp.width > providedInternalInsets.right) {
+                return lp.width - providedInternalInsets.right;
             } else {
                 return 0;
             }
         } else if (position == NAV_BAR_RIGHT) {
-            if (lp.width > lp.providedInternalInsets.left) {
-                return lp.width - lp.providedInternalInsets.left;
+            if (lp.width > providedInternalInsets.left) {
+                return lp.width - providedInternalInsets.left;
             } else {
                 return 0;
             }
@@ -1994,10 +2015,18 @@
             return 0;
         }
         LayoutParams lp = mNavigationBar.getLayoutingAttrs(rotation);
-        if (lp.height < lp.providedInternalInsets.top) {
+        final Insets providedInternalInsets;
+        if (lp.providedInternalInsets != null
+                && lp.providedInternalInsets.length > ITYPE_NAVIGATION_BAR
+                && lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] != null) {
+            providedInternalInsets = lp.providedInternalInsets[ITYPE_NAVIGATION_BAR];
+        } else {
+            providedInternalInsets = Insets.NONE;
+        }
+        if (lp.height < providedInternalInsets.top) {
             return 0;
         }
-        return lp.height - lp.providedInternalInsets.top;
+        return lp.height - providedInternalInsets.top;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index a7b3728..6162f12 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -289,6 +289,23 @@
         return adjustVisibilityForTransientTypes(originalState);
     }
 
+    /**
+     * @param type the internal type of the insets.
+     * @return {@code true} if the given type is controllable, {@code false} otherwise.
+     */
+    static boolean isInsetsTypeControllable(@InternalInsetsType int type) {
+        switch (type) {
+            case ITYPE_STATUS_BAR:
+            case ITYPE_NAVIGATION_BAR:
+            case ITYPE_IME:
+            case ITYPE_CLIMATE_BAR:
+            case ITYPE_EXTRA_NAVIGATION_BAR:
+                return true;
+            default:
+                return false;
+        }
+    }
+
     private static @InternalInsetsType int getInsetsTypeForLayoutParams(
             WindowManager.LayoutParams attrs) {
         @WindowManager.LayoutParams.WindowType int type = attrs.type;
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index f3f08b2..e04644c 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -16,11 +16,7 @@
 
 package com.android.server.wm;
 
-import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
-import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_IME;
-import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
-import static android.view.InsetsState.ITYPE_STATUS_BAR;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_INSETS;
 import static com.android.server.wm.InsetsSourceProviderProto.CAPTURED_LEASH;
@@ -128,18 +124,7 @@
         mStateController = stateController;
         mFakeControl = new InsetsSourceControl(
                 source.getType(), null /* leash */, new Point(), Insets.NONE);
-
-        switch (source.getType()) {
-            case ITYPE_STATUS_BAR:
-            case ITYPE_NAVIGATION_BAR:
-            case ITYPE_IME:
-            case ITYPE_CLIMATE_BAR:
-            case ITYPE_EXTRA_NAVIGATION_BAR:
-                mControllable = true;
-                break;
-            default:
-                mControllable = false;
-        }
+        mControllable = InsetsPolicy.isInsetsTypeControllable(source.getType());
     }
 
     InsetsSource getSource() {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index cc99f37..6f69e03 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -24,6 +24,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
+import static android.content.res.Configuration.EMPTY;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
@@ -2005,7 +2006,9 @@
             // of the activity entering PIP
             r.getDisplayContent().prepareAppTransition(TRANSIT_NONE);
 
-            final boolean singleActivity = task.getChildCount() == 1;
+            final TaskFragment organizedTf = r.getOrganizedTaskFragment();
+            // TODO: Does it make sense to only count non-finishing activities?
+            final boolean singleActivity = task.getActivityCount() == 1;
             final Task rootTask;
             if (singleActivity) {
                 rootTask = task;
@@ -2041,6 +2044,14 @@
                     task.clearLastRecentsAnimationTransaction(false /* forceRemoveOverlay */);
                 }
 
+                // The organized TaskFragment is becoming empty because this activity is reparented
+                // to a new PIP Task. In this case, we should notify the organizer about why the
+                // TaskFragment becomes empty.
+                if (organizedTf != null && organizedTf.getNonFinishingActivityCount() == 1
+                        && organizedTf.getTopNonFinishingActivity() == r) {
+                    organizedTf.mClearedTaskFragmentForPip = true;
+                }
+
                 // There are multiple activities in the task and moving the top activity should
                 // reveal/leave the other activities in their original task.
                 // On the other hand, ActivityRecord#onParentChanged takes care of setting the
@@ -2086,6 +2097,15 @@
             // TODO(task-org): Figure-out more structured way to do this long term.
             r.setWindowingMode(intermediateWindowingMode);
             r.mWaitForEnteringPinnedMode = true;
+            rootTask.forAllTaskFragments(tf -> {
+                // When the Task is entering picture-in-picture, we should clear all override from
+                // the client organizer, so the PIP activity can get the correct config from the
+                // Task, and prevent conflict with the PipTaskOrganizer.
+                if (tf.isOrganizedTaskFragment()) {
+                    tf.resetAdjacentTaskFragment();
+                    tf.updateRequestedOverrideConfiguration(EMPTY);
+                }
+            });
             rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
             // Set the launch bounds for launch-into-pip Activity on the root task.
             if (r.getOptions() != null && r.getOptions().isLaunchIntoPip()) {
@@ -2096,6 +2116,13 @@
             // Reset the state that indicates it can enter PiP while pausing after we've moved it
             // to the root pinned task
             r.supportsEnterPipOnTaskSwitch = false;
+
+            if (organizedTf != null && organizedTf.mClearedTaskFragmentForPip) {
+                // Dispatch the pending info to TaskFragmentOrganizer before PIP animation.
+                // Otherwise, it will keep waiting for the empty TaskFragment to be non-empty.
+                mService.mTaskFragmentOrganizerController.dispatchPendingInfoChangedEvent(
+                        organizedTf);
+            }
         } finally {
             mService.continueWindowLayout();
         }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a46544d..bd078d8 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1380,6 +1380,14 @@
         return getActivity(ActivityRecord::canBeTopRunning);
     }
 
+    int getActivityCount() {
+        final int[] activityCount = new int[1];
+        forAllActivities(ar -> {
+            activityCount[0]++;
+        });
+        return activityCount[0];
+    }
+
     /**
      * Return true if any activities in this task belongs to input uid.
      */
@@ -2035,7 +2043,7 @@
         Rect outOverrideBounds = getResolvedOverrideConfiguration().windowConfiguration.getBounds();
 
         if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
-            if (!mCreatedByOrganizer) {
+            if (!isOrganized()) {
                 // Use empty bounds to indicate "fill parent".
                 outOverrideBounds.setEmpty();
             }
@@ -2780,9 +2788,11 @@
         }
 
         final Rect visibleFrame = sTmpBounds;
+        final WindowManager.LayoutParams attrs = win.mAttrs;
         visibleFrame.set(win.getFrame());
         visibleFrame.inset(win.getInsetsStateWithVisibilityOverride().calculateVisibleInsets(
-                visibleFrame, win.mAttrs.softInputMode));
+                visibleFrame, attrs.type, win.getWindowingMode(), attrs.softInputMode,
+                attrs.flags));
         out.union(visibleFrame);
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index b96b461..4e0d84c 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -193,6 +193,12 @@
     boolean mClearedTaskForReuse;
 
     /**
+     * The last running activity of the TaskFragment was reparented to a different Task because it
+     * is entering PiP.
+     */
+    boolean mClearedTaskFragmentForPip;
+
+    /**
      * When we are in the process of pausing an activity, before starting the
      * next one, this variable holds the activity that is currently being paused.
      *
@@ -339,7 +345,7 @@
         }
     }
 
-    private void resetAdjacentTaskFragment() {
+    void resetAdjacentTaskFragment() {
         // Reset the adjacent TaskFragment if its adjacent TaskFragment is also this TaskFragment.
         if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) {
             mAdjacentTaskFragment.mAdjacentTaskFragment = null;
@@ -847,6 +853,16 @@
         return getActivity((r) -> r.canBeTopRunning() && r.getTask() == this.getTask());
     }
 
+    int getNonFinishingActivityCount() {
+        final int[] runningActivityCount = new int[1];
+        forAllActivities(a -> {
+            if (!a.finishing) {
+                runningActivityCount[0]++;
+            }
+        });
+        return runningActivityCount[0];
+    }
+
     boolean isTopActivityFocusable() {
         final ActivityRecord r = topRunningActivity();
         return r != null ? r.isFocusable()
@@ -1709,6 +1725,7 @@
     void addChild(WindowContainer child, int index) {
         ActivityRecord r = topRunningActivity();
         mClearedTaskForReuse = false;
+        mClearedTaskFragmentForPip = false;
 
         boolean isAddingActivity = child.asActivityRecord() != null;
         final Task task = isAddingActivity ? getTask() : null;
@@ -2253,22 +2270,16 @@
         }
         final Point positionInParent = new Point();
         getRelativePosition(positionInParent);
-        final int[] runningActivityCount = new int[1];
-        forAllActivities(a -> {
-            if (!a.finishing) {
-                runningActivityCount[0]++;
-            }
-        });
         return new TaskFragmentInfo(
                 mFragmentToken,
                 mRemoteToken.toWindowContainerToken(),
                 getConfiguration(),
-                runningActivityCount[0] == 0,
-                runningActivityCount[0],
+                getNonFinishingActivityCount(),
                 isVisible(),
                 childActivities,
                 positionInParent,
-                mClearedTaskForReuse);
+                mClearedTaskForReuse,
+                mClearedTaskFragmentForPip);
     }
 
     @Nullable
@@ -2317,6 +2328,14 @@
         mMinHeight = minHeight;
     }
 
+    /**
+     * Whether this is an embedded TaskFragment in PIP Task. We don't allow any client config
+     * override for such TaskFragment to prevent flight with PipTaskOrganizer.
+     */
+    boolean isEmbeddedTaskFragmentInPip() {
+        return isOrganizedTaskFragment() && getTask() != null && getTask().inPinnedWindowingMode();
+    }
+
     boolean shouldRemoveSelfOnLastChildRemoval() {
         return !mCreatedByOrganizer || mIsRemovalRequested;
     }
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 8a0ae65..9bf988f 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -38,6 +38,7 @@
 import android.os.RemoteException;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
+import android.view.Display;
 import android.view.SurfaceControl;
 import android.window.ITaskOrganizer;
 import android.window.ITaskOrganizerController;
@@ -526,17 +527,17 @@
         }
         final StartingWindowRemovalInfo removalInfo = new StartingWindowRemovalInfo();
         removalInfo.taskId = task.mTaskId;
-        removalInfo.playRevealAnimation = prepareAnimation;
+        removalInfo.playRevealAnimation = prepareAnimation
+                && task.getDisplayInfo().state == Display.STATE_ON;
         final boolean playShiftUpAnimation = !task.inMultiWindowMode();
         final ActivityRecord topActivity = task.topActivityContainsStartingWindow();
         if (topActivity != null) {
             removalInfo.deferRemoveForIme = topActivity.mDisplayContent
                     .mayImeShowOnLaunchingActivity(topActivity);
-            if (prepareAnimation && playShiftUpAnimation) {
+            if (removalInfo.playRevealAnimation && playShiftUpAnimation) {
                 final WindowState mainWindow =
                         topActivity.findMainWindow(false/* includeStartingApp */);
                 if (mainWindow != null) {
-                    final SurfaceControl.Transaction t = mainWindow.getPendingTransaction();
                     removalInfo.windowAnimationLeash = applyStartingWindowAnimation(mainWindow);
                     removalInfo.mainFrame = mainWindow.getRelativeFrame();
                 }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 5969e85..4dbcea1 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2761,7 +2761,10 @@
     boolean canStartChangeTransition() {
         return !mWmService.mDisableTransitionAnimation && mDisplayContent != null
                 && getSurfaceControl() != null && !mDisplayContent.inTransition()
-                && isVisible() && isVisibleRequested() && okToAnimate();
+                && isVisible() && isVisibleRequested() && okToAnimate()
+                // Pip animation will be handled by PipTaskOrganizer.
+                && !inPinnedWindowingMode() && getParent() != null
+                && !getParent().inPinnedWindowingMode();
     }
 
     /**
@@ -3860,7 +3863,8 @@
                 @AnimationType int type, @Nullable AnimationAdapter snapshotAnim);
     }
 
-    void addTrustedOverlay(SurfaceControlViewHost.SurfacePackage overlay) {
+    void addTrustedOverlay(SurfaceControlViewHost.SurfacePackage overlay,
+            @Nullable WindowState initialWindowState) {
         if (mOverlayHost == null) {
             mOverlayHost = new TrustedOverlayHost(mWmService);
         }
@@ -3876,6 +3880,20 @@
                     "Error sending initial configuration change to WindowContainer overlay");
             removeTrustedOverlay(overlay);
         }
+
+        // Emit an initial WindowState so that proper insets are available to overlay views
+        // shortly after the overlay is added.
+        if (initialWindowState != null) {
+            final InsetsState insetsState = initialWindowState.getInsetsState();
+            final Rect dispBounds = getBounds();
+            try {
+                overlay.getRemoteInterface().onInsetsChanged(insetsState, dispBounds);
+            } catch (Exception e) {
+                ProtoLog.e(WM_DEBUG_ANIM,
+                        "Error sending initial insets change to WindowContainer overlay");
+                removeTrustedOverlay(overlay);
+            }
+        }
     }
 
     void removeTrustedOverlay(SurfaceControlViewHost.SurfacePackage overlay) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index be74596..42fad6d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -409,21 +409,6 @@
     public abstract void getMagnificationRegion(int displayId, @NonNull Region magnificationRegion);
 
     /**
-     * Gets the magnification and translation applied to a window given its token.
-     * Not all windows are magnified and the window manager policy determines which
-     * windows are magnified. The returned result also takes into account the compat
-     * scale if necessary.
-     *
-     * @param windowToken The window's token.
-     *
-     * @return The magnification spec for the window.
-     *
-     * @see #setMagnificationCallbacks(int, MagnificationCallbacks)
-     */
-    public abstract MagnificationSpec getCompatibleMagnificationSpecForWindow(
-            IBinder windowToken);
-
-    /**
      * Sets a callback for observing which windows are touchable for the purposes
      * of accessibility on specified display.
      *
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 77d31df..2f4dc51 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1976,8 +1976,10 @@
         // We use the visible frame, because we want the animation to morph the window from what
         // was visible to the user to the final destination of the new window.
         final Rect frame = new Rect(replacedWindow.getFrame());
+        final WindowManager.LayoutParams attrs = replacedWindow.mAttrs;
         frame.inset(replacedWindow.getInsetsStateWithVisibilityOverride().calculateVisibleInsets(
-                frame, replacedWindow.mAttrs.softInputMode));
+                frame, attrs.type, replacedWindow.getWindowingMode(), attrs.softInputMode,
+                attrs.flags));
         // We treat this as if this activity was opening, so we can trigger the app transition
         // animation and piggy-back on existing transition animation infrastructure.
         final DisplayContent dc = activity.getDisplayContent();
@@ -7621,29 +7623,6 @@
         }
 
         @Override
-        public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) {
-            synchronized (mGlobalLock) {
-                WindowState windowState = mWindowMap.get(windowToken);
-                if (windowState == null) {
-                    return null;
-                }
-                MagnificationSpec spec = null;
-                if (mAccessibilityController.hasCallbacks()) {
-                    spec = mAccessibilityController.getMagnificationSpecForWindow(windowState);
-                }
-                if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) {
-                    return null;
-                }
-                MagnificationSpec result = new MagnificationSpec();
-                if (spec != null) {
-                    result.setTo(spec);
-                }
-                result.scale *= windowState.mGlobalScale;
-                return result;
-            }
-        }
-
-        @Override
         public boolean setMagnificationCallbacks(int displayId,
                 @Nullable MagnificationCallbacks callbacks) {
             synchronized (mGlobalLock) {
@@ -8135,7 +8114,7 @@
                 if (task == null) {
                     throw new IllegalArgumentException("no task with taskId" + taskId);
                 }
-                task.addTrustedOverlay(overlay);
+                task.addTrustedOverlay(overlay, task.getTopVisibleAppMainWindow());
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index b5cf708..c1c8b81 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -677,15 +677,21 @@
             }
             case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: {
                 final IBinder fragmentToken = hop.getContainer();
-                if (!mLaunchTaskFragments.containsKey(fragmentToken)) {
+                final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken);
+                if (tf == null) {
                     final Throwable exception = new IllegalArgumentException(
                             "Not allowed to operate with invalid fragment token");
                     sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
                     break;
                 }
+                if (tf.isEmbeddedTaskFragmentInPip()) {
+                    final Throwable exception = new IllegalArgumentException(
+                            "Not allowed to start activity in PIP TaskFragment");
+                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+                    break;
+                }
                 final Intent activityIntent = hop.getActivityIntent();
                 final Bundle activityOptions = hop.getLaunchOptions();
-                final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken);
                 final int result = mService.getActivityStartController()
                         .startActivityInTaskFragment(tf, activityIntent, activityOptions,
                                 hop.getCallingActivity(), caller.mUid, caller.mPid);
@@ -707,6 +713,12 @@
                     sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
                     break;
                 }
+                if (parent.isEmbeddedTaskFragmentInPip()) {
+                    final Throwable exception = new IllegalArgumentException(
+                            "Not allowed to reparent activity to PIP TaskFragment");
+                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+                    break;
+                }
                 if (!parent.isAllowedToEmbedActivity(activity)) {
                     final Throwable exception = new SecurityException(
                             "The task fragment is not trusted to embed the given activity.");
@@ -730,6 +742,13 @@
                     sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
                     break;
                 }
+                if (tf1.isEmbeddedTaskFragmentInPip()
+                        || (tf2 != null && tf2.isEmbeddedTaskFragmentInPip())) {
+                    final Throwable exception = new IllegalArgumentException(
+                            "Not allowed to set adjacent on TaskFragment in PIP Task");
+                    sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+                    break;
+                }
                 tf1.setAdjacentTaskFragment(tf2, false /* moveAdjacentTogether */);
                 effects |= TRANSACT_EFFECTS_LIFECYCLE;
 
@@ -1092,6 +1111,10 @@
             throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by"
                     + " organizer root1=" + root1 + " root2=" + root2);
         }
+        if (root1.isEmbeddedTaskFragmentInPip() || root2.isEmbeddedTaskFragmentInPip()) {
+            Slog.e(TAG, "Attempt to set adjacent TaskFragment in PIP Task");
+            return 0;
+        }
         root1.setAdjacentTaskFragment(root2, hop.getMoveAdjacentTogether());
         return TRANSACT_EFFECTS_LIFECYCLE;
     }
@@ -1105,6 +1128,10 @@
     private int applyWindowContainerChange(WindowContainer wc,
             WindowContainerTransaction.Change c) {
         sanitizeWindowContainer(wc);
+        if (wc.asTaskFragment() != null && wc.asTaskFragment().isEmbeddedTaskFragmentInPip()) {
+            // No override from organizer for embedded TaskFragment in a PIP Task.
+            return 0;
+        }
 
         int effects = applyChanges(wc, c);
 
@@ -1420,21 +1447,28 @@
             return;
         }
         // The ownerActivity has to belong to the same app as the target Task.
-        if (ownerActivity.getTask().effectiveUid != ownerActivity.getUid()
-                || ownerActivity.getTask().effectiveUid != caller.mUid) {
+        final Task ownerTask = ownerActivity.getTask();
+        if (ownerTask.effectiveUid != ownerActivity.getUid()
+                || ownerTask.effectiveUid != caller.mUid) {
             final Throwable exception =
                     new SecurityException("Not allowed to operate with the ownerToken while "
                             + "the root activity of the target task belong to the different app");
             sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
             return;
         }
+        if (ownerTask.inPinnedWindowingMode()) {
+            final Throwable exception = new IllegalArgumentException(
+                    "Not allowed to create TaskFragment in PIP Task");
+            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+            return;
+        }
         final TaskFragment taskFragment = new TaskFragment(mService,
                 creationParams.getFragmentToken(), true /* createdByOrganizer */);
         // Set task fragment organizer immediately, since it might have to be notified about further
         // actions.
         taskFragment.setTaskFragmentOrganizer(creationParams.getOrganizer(),
                 ownerActivity.getUid(), ownerActivity.info.processName);
-        ownerActivity.getTask().addChild(taskFragment, POSITION_TOP);
+        ownerTask.addChild(taskFragment, POSITION_TOP);
         taskFragment.setWindowingMode(creationParams.getWindowingMode());
         taskFragment.setBounds(creationParams.getInitialBounds());
         mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment);
@@ -1467,6 +1501,12 @@
                 return;
             }
         }
+        if (newParentTF.isEmbeddedTaskFragmentInPip() || oldParent.isEmbeddedTaskFragmentInPip()) {
+            final Throwable exception = new SecurityException(
+                    "Not allow to reparent in TaskFragment in PIP Task.");
+            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+            return;
+        }
         while (oldParent.hasChild()) {
             oldParent.getChildAt(0).reparent(newParentTF, POSITION_TOP);
         }
@@ -1482,6 +1522,12 @@
             sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
             return 0;
         }
+        if (taskFragment.isEmbeddedTaskFragmentInPip()) {
+            final Throwable exception = new IllegalArgumentException(
+                    "Not allowed to delete TaskFragment in PIP Task");
+            sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+            return 0;
+        }
         mLaunchTaskFragments.removeAt(index);
         taskFragment.remove(true /* withTransition */, "deleteTaskFragment");
         return TRANSACT_EFFECTS_LIFECYCLE;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e7d4877..238f96f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1851,7 +1851,7 @@
 
         bounds.set(mWindowFrames.mFrame);
         bounds.inset(getInsetsStateWithVisibilityOverride().calculateVisibleInsets(
-                bounds, mAttrs.softInputMode));
+                bounds, mAttrs.type, getWindowingMode(), mAttrs.softInputMode, mAttrs.flags));
         if (intersectWithRootTaskBounds) {
             bounds.intersect(mTmpRect);
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java
index 82fe8b9..6aef90c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java
@@ -55,8 +55,6 @@
     private static final String TAG_ROOT = "root";
     private static final String TAG_DRAWABLE_STYLE_ENTRY = "drawable-style-entry";
     private static final String TAG_DRAWABLE_SOURCE_ENTRY = "drawable-source-entry";
-    private static final String ATTR_DRAWABLE_STYLE_SIZE = "drawable-style-size";
-    private static final String ATTR_DRAWABLE_SOURCE_SIZE = "drawable-source-size";
     private static final String ATTR_DRAWABLE_STYLE = "drawable-style";
     private static final String ATTR_DRAWABLE_SOURCE = "drawable-source";
     private static final String ATTR_DRAWABLE_ID = "drawable-id";
@@ -70,9 +68,9 @@
             mUpdatedDrawablesForStyle = new HashMap<>();
 
     /**
-     * Map of <drawable_id, <source_id, resource_value>>
+     * Map of <drawable_id, <source_id, <style_id, resource_value>>>
      */
-    private final Map<String, Map<String, ParcelableResource>>
+    private final Map<String, Map<String, Map<String, ParcelableResource>>>
             mUpdatedDrawablesForSource = new HashMap<>();
 
     /**
@@ -110,7 +108,8 @@
             if (DevicePolicyResources.UNDEFINED.equals(drawableSource)) {
                 updated |= updateDrawable(drawableId, drawableStyle, resource);
             } else {
-                updated |= updateDrawableForSource(drawableId, drawableSource, resource);
+                updated |= updateDrawableForSource(
+                        drawableId, drawableSource, drawableStyle, resource);
             }
         }
         if (!updated) {
@@ -138,19 +137,23 @@
         }
     }
 
-    // TODO(b/214576716): change this to respect style
     private boolean updateDrawableForSource(
-            String drawableId, String drawableSource, ParcelableResource updatableResource) {
+            String drawableId, String drawableSource, String drawableStyle,
+            ParcelableResource updatableResource) {
         synchronized (mLock) {
+            Map<String, Map<String, ParcelableResource>> drawablesForId =
+                    mUpdatedDrawablesForSource.get(drawableId);
             if (!mUpdatedDrawablesForSource.containsKey(drawableId)) {
                 mUpdatedDrawablesForSource.put(drawableId, new HashMap<>());
             }
-            ParcelableResource current = mUpdatedDrawablesForSource.get(drawableId).get(
-                    drawableSource);
+            if (!drawablesForId.containsKey(drawableSource)) {
+                mUpdatedDrawablesForSource.get(drawableId).put(drawableSource, new HashMap<>());
+            }
+            ParcelableResource current = drawablesForId.get(drawableSource).get(drawableStyle);
             if (updatableResource.equals(current)) {
                 return false;
             }
-            mUpdatedDrawablesForSource.get(drawableId).put(drawableSource, updatableResource);
+            drawablesForId.get(drawableSource).put(drawableStyle, updatableResource);
             return true;
         }
     }
@@ -175,23 +178,30 @@
     }
 
     @Nullable
-    ParcelableResource getDrawable(
-            String drawableId, String drawableStyle, String drawableSource) {
+    ParcelableResource getDrawable(String drawableId, String drawableStyle, String drawableSource) {
         synchronized (mLock) {
-            if (mUpdatedDrawablesForSource.containsKey(drawableId)
-                    && mUpdatedDrawablesForSource.get(drawableId).containsKey(drawableSource)) {
-                return mUpdatedDrawablesForSource.get(drawableId).get(drawableSource);
+            ParcelableResource resource = getDrawableForSourceLocked(
+                    drawableId, drawableStyle, drawableSource);
+            if (resource != null) {
+                return resource;
             }
             if (!mUpdatedDrawablesForStyle.containsKey(drawableId)) {
-                Log.d(TAG, "No updated drawable found for drawable id " + drawableId);
                 return null;
             }
-            if (mUpdatedDrawablesForStyle.get(drawableId).containsKey(drawableStyle)) {
-                return mUpdatedDrawablesForStyle.get(drawableId).get(drawableStyle);
-            }
+            return mUpdatedDrawablesForStyle.get(drawableId).get(drawableStyle);
         }
-        Log.d(TAG, "No updated drawable found for drawable id " + drawableId);
-        return null;
+    }
+
+    @Nullable
+    ParcelableResource getDrawableForSourceLocked(
+            String drawableId, String drawableStyle, String drawableSource) {
+        if (!mUpdatedDrawablesForSource.containsKey(drawableId)) {
+            return null;
+        }
+        if (!mUpdatedDrawablesForSource.get(drawableId).containsKey(drawableSource)) {
+            return null;
+        }
+        return mUpdatedDrawablesForSource.get(drawableId).get(drawableSource).get(drawableStyle);
     }
 
     /**
@@ -249,12 +259,8 @@
     @Nullable
     ParcelableResource getString(String stringId) {
         synchronized (mLock) {
-            if (mUpdatedStrings.containsKey(stringId)) {
-                return mUpdatedStrings.get(stringId);
-            }
+            return mUpdatedStrings.get(stringId);
         }
-        Log.d(TAG, "No updated string found for string id " + stringId);
-        return null;
     }
 
     private void write() {
@@ -359,50 +365,55 @@
         }
 
         void writeInner(TypedXmlSerializer out) throws IOException {
+            writeDrawablesForStylesInner(out);
+            writeDrawablesForSourcesInner(out);
+            writeStringsInner(out);
+        }
+
+        private void writeDrawablesForStylesInner(TypedXmlSerializer out) throws IOException {
             if (mUpdatedDrawablesForStyle != null && !mUpdatedDrawablesForStyle.isEmpty()) {
                 for (Map.Entry<String, Map<String, ParcelableResource>> drawableEntry
                         : mUpdatedDrawablesForStyle.entrySet()) {
-                    out.startTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY);
-                    out.attribute(
-                            /* namespace= */ null, ATTR_DRAWABLE_ID, drawableEntry.getKey());
-                    out.attributeInt(
-                            /* namespace= */ null,
-                            ATTR_DRAWABLE_STYLE_SIZE,
-                            drawableEntry.getValue().size());
-                    int counter = 0;
                     for (Map.Entry<String, ParcelableResource> styleEntry
                             : drawableEntry.getValue().entrySet()) {
+                        out.startTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY);
+                        out.attribute(
+                                /* namespace= */ null, ATTR_DRAWABLE_ID, drawableEntry.getKey());
                         out.attribute(
                                 /* namespace= */ null,
-                                ATTR_DRAWABLE_STYLE + (counter++),
+                                ATTR_DRAWABLE_STYLE,
                                 styleEntry.getKey());
                         styleEntry.getValue().writeToXmlFile(out);
+                        out.endTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY);
                     }
-                    out.endTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY);
                 }
             }
+        }
+
+        private void writeDrawablesForSourcesInner(TypedXmlSerializer out) throws IOException {
             if (mUpdatedDrawablesForSource != null && !mUpdatedDrawablesForSource.isEmpty()) {
-                for (Map.Entry<String, Map<String, ParcelableResource>> drawableEntry
+                for (Map.Entry<String, Map<String, Map<String, ParcelableResource>>> drawableEntry
                         : mUpdatedDrawablesForSource.entrySet()) {
-                    out.startTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY);
-                    out.attribute(
-                            /* namespace= */ null, ATTR_DRAWABLE_ID, drawableEntry.getKey());
-                    out.attributeInt(
-                            /* namespace= */ null,
-                            ATTR_DRAWABLE_SOURCE_SIZE,
-                            drawableEntry.getValue().size());
-                    int counter = 0;
-                    for (Map.Entry<String, ParcelableResource> sourceEntry
+                    for (Map.Entry<String, Map<String, ParcelableResource>> sourceEntry
                             : drawableEntry.getValue().entrySet()) {
-                        out.attribute(
-                                /* namespace= */ null,
-                                ATTR_DRAWABLE_SOURCE + (counter++),
-                                sourceEntry.getKey());
-                        sourceEntry.getValue().writeToXmlFile(out);
+                        for (Map.Entry<String, ParcelableResource> styleEntry
+                                : sourceEntry.getValue().entrySet()) {
+                            out.startTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY);
+                            out.attribute(/* namespace= */ null, ATTR_DRAWABLE_ID,
+                                    drawableEntry.getKey());
+                            out.attribute(/* namespace= */ null, ATTR_DRAWABLE_SOURCE,
+                                    sourceEntry.getKey());
+                            out.attribute(/* namespace= */ null, ATTR_DRAWABLE_STYLE,
+                                    styleEntry.getKey());
+                            styleEntry.getValue().writeToXmlFile(out);
+                            out.endTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY);
+                        }
                     }
-                    out.endTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY);
                 }
             }
+        }
+
+        private void writeStringsInner(TypedXmlSerializer out) throws IOException {
             if (mUpdatedStrings != null && !mUpdatedStrings.isEmpty()) {
                 for (Map.Entry<String, ParcelableResource> entry
                         : mUpdatedStrings.entrySet()) {
@@ -417,52 +428,48 @@
             }
         }
 
-        private boolean readInner(
-                TypedXmlPullParser parser, int depth, String tag)
+        private boolean readInner(TypedXmlPullParser parser, int depth, String tag)
                 throws XmlPullParserException, IOException {
             if (depth > 2) {
                 return true; // Ignore
             }
             switch (tag) {
-                case TAG_DRAWABLE_STYLE_ENTRY:
-                    String drawableId = parser.getAttributeValue(
-                            /* namespace= */ null, ATTR_DRAWABLE_ID);
-                    mUpdatedDrawablesForStyle.put(
-                            drawableId,
-                            new HashMap<>());
-                    int size = parser.getAttributeInt(
-                            /* namespace= */ null, ATTR_DRAWABLE_STYLE_SIZE);
-                    for (int i = 0; i < size; i++) {
-                        String style = parser.getAttributeValue(
-                                /* namespace= */ null, ATTR_DRAWABLE_STYLE + i);
-                        mUpdatedDrawablesForStyle.get(drawableId).put(
-                                style,
-                                ParcelableResource.createFromXml(parser));
+                case TAG_DRAWABLE_STYLE_ENTRY: {
+                    String id = parser.getAttributeValue(/* namespace= */ null, ATTR_DRAWABLE_ID);
+                    String style = parser.getAttributeValue(
+                            /* namespace= */ null, ATTR_DRAWABLE_STYLE);
+                    ParcelableResource resource = ParcelableResource.createFromXml(parser);
+                    if (!mUpdatedDrawablesForStyle.containsKey(id)) {
+                        mUpdatedDrawablesForStyle.put(id, new HashMap<>());
                     }
+                    mUpdatedDrawablesForStyle.get(id).put(style, resource);
                     break;
-                case TAG_DRAWABLE_SOURCE_ENTRY:
-                    drawableId = parser.getAttributeValue(
-                            /* namespace= */ null, ATTR_DRAWABLE_ID);
-                    mUpdatedDrawablesForSource.put(drawableId, new HashMap<>());
-                    size = parser.getAttributeInt(
-                            /* namespace= */ null, ATTR_DRAWABLE_SOURCE_SIZE);
-                    for (int i = 0; i < size; i++) {
-                        String source = parser.getAttributeValue(
-                                /* namespace= */ null, ATTR_DRAWABLE_SOURCE + i);
-                        mUpdatedDrawablesForSource.get(drawableId).put(
-                                source,
-                                ParcelableResource.createFromXml(parser));
+                }
+                case TAG_DRAWABLE_SOURCE_ENTRY: {
+                    String id = parser.getAttributeValue(/* namespace= */ null, ATTR_DRAWABLE_ID);
+                    String source = parser.getAttributeValue(
+                            /* namespace= */ null, ATTR_DRAWABLE_SOURCE);
+                    String style = parser.getAttributeValue(
+                            /* namespace= */ null, ATTR_DRAWABLE_STYLE);
+                    ParcelableResource resource = ParcelableResource.createFromXml(parser);
+                    if (!mUpdatedDrawablesForSource.containsKey(id)) {
+                        mUpdatedDrawablesForSource.put(id, new HashMap<>());
                     }
+                    if (!mUpdatedDrawablesForSource.get(id).containsKey(source)) {
+                        mUpdatedDrawablesForSource.get(id).put(source, new HashMap<>());
+                    }
+                    mUpdatedDrawablesForSource.get(id).get(source).put(style, resource);
                     break;
-                case TAG_STRING_ENTRY:
-                    String sourceId = parser.getAttributeValue(
-                            /* namespace= */ null, ATTR_SOURCE_ID);
-                    mUpdatedStrings.put(
-                            sourceId, ParcelableResource.createFromXml(parser));
+                }
+                case TAG_STRING_ENTRY: {
+                    String id = parser.getAttributeValue(/* namespace= */ null, ATTR_SOURCE_ID);
+                    mUpdatedStrings.put(id, ParcelableResource.createFromXml(parser));
                     break;
-                default:
+                }
+                default: {
                     Log.e(TAG, "Unexpected tag: " + tag);
                     return false;
+                }
             }
             return true;
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 268d588..18bffeb 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -382,8 +382,6 @@
 import com.android.server.utils.Slogf;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
-import com.google.android.collect.Sets;
-
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.ByteArrayInputStream;
@@ -1781,7 +1779,7 @@
 
     @VisibleForTesting
     DevicePolicyManagerService(Injector injector) {
-        DevicePolicyManager.disableGetKeyguardDisabledFeaturesCache();
+        DevicePolicyManager.disableLocalCaches();
 
         mInjector = injector;
         mContext = Objects.requireNonNull(injector.mContext);
@@ -2002,7 +2000,6 @@
         synchronized (getLockObject()) {
             mOwners.load();
             setDeviceOwnershipSystemPropertyLocked();
-            findOwnerComponentIfNecessaryLocked();
         }
     }
 
@@ -2387,139 +2384,6 @@
         }
     }
 
-    private void findOwnerComponentIfNecessaryLocked() {
-        if (!mOwners.hasDeviceOwner()) {
-            return;
-        }
-        final ComponentName doComponentName = mOwners.getDeviceOwnerComponent();
-
-        if (!TextUtils.isEmpty(doComponentName.getClassName())) {
-            return; // Already a full component name.
-        }
-
-        final ComponentName doComponent = findAdminComponentWithPackageLocked(
-                doComponentName.getPackageName(),
-                mOwners.getDeviceOwnerUserId());
-        if (doComponent == null) {
-            Slogf.e(LOG_TAG, "Device-owner isn't registered as device-admin");
-        } else {
-            mOwners.setDeviceOwnerWithRestrictionsMigrated(
-                    doComponent,
-                    mOwners.getDeviceOwnerName(),
-                    mOwners.getDeviceOwnerUserId(),
-                    !mOwners.getDeviceOwnerUserRestrictionsNeedsMigration());
-            mOwners.writeDeviceOwner();
-            if (VERBOSE_LOG) {
-                Slogf.v(LOG_TAG, "Device owner component filled in");
-            }
-        }
-    }
-
-    /**
-     * We didn't use to persist user restrictions for each owners but only persisted in user
-     * manager.
-     */
-    private void migrateUserRestrictionsIfNecessaryLocked() {
-        boolean migrated = false;
-        // Migrate for the DO.  Basically all restrictions should be considered to be set by DO,
-        // except for the "system controlled" ones.
-        if (mOwners.getDeviceOwnerUserRestrictionsNeedsMigration()) {
-            if (VERBOSE_LOG) {
-                Slogf.v(LOG_TAG, "Migrating DO user restrictions");
-            }
-            migrated = true;
-
-            // Migrate user 0 restrictions to DO.
-            final ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked();
-
-            migrateUserRestrictionsForUser(UserHandle.SYSTEM, deviceOwnerAdmin,
-                    /* exceptionList =*/ null, /* isDeviceOwner =*/ true);
-
-            // Push DO user restrictions to user manager.
-            pushUserRestrictions(UserHandle.USER_SYSTEM);
-
-            mOwners.setDeviceOwnerUserRestrictionsMigrated();
-        }
-
-        // Migrate for POs.
-
-        // The following restrictions can be set on secondary users by the device owner, so we
-        // assume they're not from the PO.
-        final Set<String> secondaryUserExceptionList = Sets.newArraySet(
-                UserManager.DISALLOW_OUTGOING_CALLS,
-                UserManager.DISALLOW_SMS);
-
-        for (UserInfo ui : mUserManager.getUsers()) {
-            final int userId = ui.id;
-            if (mOwners.getProfileOwnerUserRestrictionsNeedsMigration(userId)) {
-                if (VERBOSE_LOG) {
-                    Slogf.v(LOG_TAG, "Migrating PO user restrictions for user %d", userId);
-                }
-                migrated = true;
-
-                final ActiveAdmin profileOwnerAdmin = getProfileOwnerAdminLocked(userId);
-
-                final Set<String> exceptionList =
-                        (userId == UserHandle.USER_SYSTEM) ? null : secondaryUserExceptionList;
-
-                migrateUserRestrictionsForUser(ui.getUserHandle(), profileOwnerAdmin,
-                        exceptionList, /* isDeviceOwner =*/ false);
-
-                // Note if a secondary user has no PO but has a DA that disables camera, we
-                // don't get here and won't push the camera user restriction to UserManager
-                // here.  That's okay because we'll push user restrictions anyway when a user
-                // starts.  But we still do it because we want to let user manager persist
-                // upon migration.
-                pushUserRestrictions(userId);
-
-                mOwners.setProfileOwnerUserRestrictionsMigrated(userId);
-            }
-        }
-        if (VERBOSE_LOG && migrated) {
-            Slogf.v(LOG_TAG, "User restrictions migrated.");
-        }
-    }
-
-    private void migrateUserRestrictionsForUser(UserHandle user, ActiveAdmin admin,
-            Set<String> exceptionList, boolean isDeviceOwner) {
-        final Bundle origRestrictions = mUserManagerInternal.getBaseUserRestrictions(
-                user.getIdentifier());
-
-        final Bundle newBaseRestrictions = new Bundle();
-        final Bundle newOwnerRestrictions = new Bundle();
-
-        for (String key : origRestrictions.keySet()) {
-            if (!origRestrictions.getBoolean(key)) {
-                continue;
-            }
-            final boolean canOwnerChange = isDeviceOwner
-                    ? UserRestrictionsUtils.canDeviceOwnerChange(key)
-                    : UserRestrictionsUtils.canProfileOwnerChange(key, user.getIdentifier());
-
-            if (!canOwnerChange || (exceptionList!= null && exceptionList.contains(key))) {
-                newBaseRestrictions.putBoolean(key, true);
-            } else {
-                newOwnerRestrictions.putBoolean(key, true);
-            }
-        }
-
-        if (VERBOSE_LOG) {
-            Slogf.v(LOG_TAG, "origRestrictions=%s", origRestrictions);
-            Slogf.v(LOG_TAG, "newBaseRestrictions=%s", newBaseRestrictions);
-            Slogf.v(LOG_TAG, "newOwnerRestrictions=%s", newOwnerRestrictions);
-        }
-        mUserManagerInternal.setBaseUserRestrictionsByDpmsForMigration(user.getIdentifier(),
-                newBaseRestrictions);
-
-        if (admin != null) {
-            admin.ensureUserRestrictions().clear();
-            admin.ensureUserRestrictions().putAll(newOwnerRestrictions);
-        } else {
-            Slogf.w(LOG_TAG, "ActiveAdmin for DO/PO not found. user=" + user.getIdentifier());
-        }
-        saveSettingsLocked(user.getIdentifier());
-    }
-
     /**
      * Fix left-over restrictions and auto-time policy during COMP -> COPE migration.
      *
@@ -2555,27 +2419,6 @@
         }
     }
 
-    private ComponentName findAdminComponentWithPackageLocked(String packageName, int userId) {
-        final DevicePolicyData policy = getUserData(userId);
-        final int n = policy.mAdminList.size();
-        ComponentName found = null;
-        int nFound = 0;
-        for (int i = 0; i < n; i++) {
-            final ActiveAdmin admin = policy.mAdminList.get(i);
-            if (packageName.equals(admin.info.getPackageName())) {
-                // Found!
-                if (nFound == 0) {
-                    found = admin.info.getComponent();
-                }
-                nFound++;
-            }
-        }
-        if (nFound > 1) {
-            Slogf.w(LOG_TAG, "Multiple DA found; assume the first one is DO.");
-        }
-        return found;
-    }
-
     /**
      * Set an alarm for an upcoming event - expiration warning, expiration, or post-expiration
      * reminders.  Clears alarm if no expirations are configured.
@@ -3197,7 +3040,6 @@
 
     private void onLockSettingsReady() {
         synchronized (getLockObject()) {
-            migrateUserRestrictionsIfNecessaryLocked();
             fixupAutoTimeRestrictionDuringOrganizationOwnedDeviceMigration();
         }
         getUserData(UserHandle.USER_SYSTEM);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 37da7b4..859183c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -79,8 +79,6 @@
 
     private static final boolean DEBUG = false; // DO NOT SUBMIT WITH TRUE
 
-    private static final String DEVICE_OWNER_XML_LEGACY = "device_owner.xml";
-
     // XML storing device owner info, system update policy and pending OTA update information.
     private static final String DEVICE_OWNER_XML = "device_owner_2.xml";
 
@@ -89,7 +87,6 @@
     private static final String TAG_ROOT = "root";
 
     private static final String TAG_DEVICE_OWNER = "device-owner";
-    private static final String TAG_DEVICE_INITIALIZER = "device-initializer";
     private static final String TAG_SYSTEM_UPDATE_POLICY = "system-update-policy";
     private static final String TAG_FREEZE_PERIOD_RECORD = "freeze-record";
     private static final String TAG_PENDING_OTA_INFO = "pending-ota-info";
@@ -107,7 +104,6 @@
     private static final String ATTR_REMOTE_BUGREPORT_URI = "remoteBugreportUri";
     private static final String ATTR_REMOTE_BUGREPORT_HASH = "remoteBugreportHash";
     private static final String ATTR_USERID = "userId";
-    private static final String ATTR_USER_RESTRICTIONS_MIGRATED = "userRestrictionsMigrated";
     private static final String ATTR_FREEZE_RECORD_START = "start";
     private static final String ATTR_FREEZE_RECORD_END = "end";
     // Legacy attribute, its presence would mean the profile owner associated with it is
@@ -180,35 +176,14 @@
      */
     void load() {
         synchronized (mLock) {
-            // First, try to read from the legacy file.
-            final File legacy = getLegacyConfigFile();
-
             final List<UserInfo> users = mUserManager.getAliveUsers();
 
-            if (readLegacyOwnerFileLocked(legacy)) {
-                if (DEBUG) {
-                    Log.d(TAG, "Legacy config file found.");
-                }
+            new DeviceOwnerReadWriter().readFromFileLocked();
 
-                // Legacy file exists, write to new files and remove the legacy one.
-                writeDeviceOwner();
-                for (int userId : getProfileOwnerKeys()) {
-                    writeProfileOwner(userId);
-                }
-                if (DEBUG) {
-                    Log.d(TAG, "Deleting legacy config file");
-                }
-                if (!legacy.delete()) {
-                    Slog.e(TAG, "Failed to remove the legacy setting file");
-                }
-            } else {
-                // No legacy file, read from the new format files.
-                new DeviceOwnerReadWriter().readFromFileLocked();
-
-                for (UserInfo ui : users) {
-                    new ProfileOwnerReadWriter(ui.id).readFromFileLocked();
-                }
+            for (UserInfo ui : users) {
+                new ProfileOwnerReadWriter(ui.id).readFromFileLocked();
             }
+
             mUserManagerInternal.setDeviceManaged(hasDeviceOwner());
             for (UserInfo ui : users) {
                 mUserManagerInternal.setUserManaged(ui.id, hasProfileOwner(ui.id));
@@ -338,23 +313,11 @@
             return;
         }
         synchronized (mLock) {
-            // For a newly set DO, there's no need for migration.
-            setDeviceOwnerWithRestrictionsMigrated(admin, ownerName, userId,
-                    /* userRestrictionsMigrated =*/ true);
-        }
-    }
-
-    // Note this should be only called during migration.  Normally when DO is set,
-    // userRestrictionsMigrated should always be true.
-    void setDeviceOwnerWithRestrictionsMigrated(ComponentName admin, String ownerName, int userId,
-            boolean userRestrictionsMigrated) {
-        synchronized (mLock) {
             // A device owner is allowed to access device identifiers. Even though this flag
             // is not currently checked for device owner, it is set to true here so that it is
             // semantically compatible with the meaning of this flag.
-            mDeviceOwner = new OwnerInfo(ownerName, admin, userRestrictionsMigrated,
-                    /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/
-                    null, /* isOrganizationOwnedDevice =*/true);
+            mDeviceOwner = new OwnerInfo(ownerName, admin, /* remoteBugreportUri =*/ null,
+                    /* remoteBugreportHash =*/ null, /* isOrganizationOwnedDevice =*/ true);
             mDeviceOwnerUserId = userId;
 
             mUserManagerInternal.setDeviceManaged(true);
@@ -385,8 +348,8 @@
         synchronized (mLock) {
             // For a newly set PO, there's no need for migration.
             mProfileOwners.put(userId, new OwnerInfo(ownerName, admin,
-                    /* userRestrictionsMigrated =*/ true, /* remoteBugreportUri =*/ null,
-                    /* remoteBugreportHash =*/ null, /* isOrganizationOwnedDevice =*/ false));
+                    /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/ null,
+                    /* isOrganizationOwnedDevice =*/ false));
             mUserManagerInternal.setUserManaged(userId, true);
             notifyChangeLocked();
         }
@@ -404,8 +367,7 @@
         synchronized (mLock) {
             final OwnerInfo ownerInfo = mProfileOwners.get(userId);
             final OwnerInfo newOwnerInfo = new OwnerInfo(target.getPackageName(), target,
-                    ownerInfo.userRestrictionsMigrated, ownerInfo.remoteBugreportUri,
-                    ownerInfo.remoteBugreportHash, /* isOrganizationOwnedDevice =*/
+                    ownerInfo.remoteBugreportUri, ownerInfo.remoteBugreportHash,
                     ownerInfo.isOrganizationOwnedDevice);
             mProfileOwners.put(userId, newOwnerInfo);
             notifyChangeLocked();
@@ -423,10 +385,8 @@
             }
             // We don't set a name because it's not used anyway.
             // See DevicePolicyManagerService#getDeviceOwnerName
-            mDeviceOwner = new OwnerInfo(null, target,
-                    mDeviceOwner.userRestrictionsMigrated, mDeviceOwner.remoteBugreportUri,
-                    mDeviceOwner.remoteBugreportHash, /* isOrganizationOwnedDevice =*/
-                    mDeviceOwner.isOrganizationOwnedDevice);
+            mDeviceOwner = new OwnerInfo(null, target, mDeviceOwner.remoteBugreportUri,
+                    mDeviceOwner.remoteBugreportHash, mDeviceOwner.isOrganizationOwnedDevice);
             if (previousDeviceOwnerType != null) {
                 mDeviceOwnerTypes.put(mDeviceOwner.packageName, previousDeviceOwnerType);
             }
@@ -570,35 +530,6 @@
         }
     }
 
-    /**
-     * @return true if user restrictions need to be migrated for DO.
-     */
-    boolean getDeviceOwnerUserRestrictionsNeedsMigration() {
-        synchronized (mLock) {
-            return mDeviceOwner != null && !mDeviceOwner.userRestrictionsMigrated;
-        }
-    }
-
-    /**
-     * @return true if user restrictions need to be migrated for PO.
-     */
-    boolean getProfileOwnerUserRestrictionsNeedsMigration(int userId) {
-        synchronized (mLock) {
-            OwnerInfo profileOwner = mProfileOwners.get(userId);
-            return profileOwner != null && !profileOwner.userRestrictionsMigrated;
-        }
-    }
-
-    /** Sets the user restrictions migrated flag, and also writes to the file. */
-    void setDeviceOwnerUserRestrictionsMigrated() {
-        synchronized (mLock) {
-            if (mDeviceOwner != null) {
-                mDeviceOwner.userRestrictionsMigrated = true;
-            }
-            writeDeviceOwner();
-        }
-    }
-
     /** Sets the remote bugreport uri and hash, and also writes to the file. */
     void setDeviceOwnerRemoteBugreportUriAndHash(String remoteBugreportUri,
             String remoteBugreportHash) {
@@ -611,17 +542,6 @@
         }
     }
 
-    /** Sets the user restrictions migrated flag, and also writes to the file. */
-    void setProfileOwnerUserRestrictionsMigrated(int userId) {
-        synchronized (mLock) {
-            OwnerInfo profileOwner = mProfileOwners.get(userId);
-            if (profileOwner != null) {
-                profileOwner.userRestrictionsMigrated = true;
-            }
-            writeProfileOwner(userId);
-        }
-    }
-
     /** Set whether the profile owner manages an organization-owned device, then write to file. */
     void setProfileOwnerOfOrganizationOwnedDevice(int userId, boolean isOrganizationOwnedDevice) {
         synchronized (mLock) {
@@ -696,72 +616,6 @@
         }
     }
 
-    private boolean readLegacyOwnerFileLocked(File file) {
-        if (!file.exists()) {
-            // Already migrated or the device has no owners.
-            return false;
-        }
-        try {
-            InputStream input = new AtomicFile(file).openRead();
-            TypedXmlPullParser parser = Xml.resolvePullParser(input);
-            int type;
-            while ((type = parser.next()) != TypedXmlPullParser.END_DOCUMENT) {
-                if (type != TypedXmlPullParser.START_TAG) {
-                    continue;
-                }
-
-                String tag = parser.getName();
-                if (tag.equals(TAG_DEVICE_OWNER)) {
-                    String name = parser.getAttributeValue(null, ATTR_NAME);
-                    String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
-                    mDeviceOwner = new OwnerInfo(name, packageName,
-                            /* userRestrictionsMigrated =*/ false, /* remoteBugreportUri =*/ null,
-                            /* remoteBugreportHash =*/ null, /* isOrganizationOwnedDevice =*/ true);
-                    mDeviceOwnerUserId = UserHandle.USER_SYSTEM;
-                } else if (tag.equals(TAG_DEVICE_INITIALIZER)) {
-                    // Deprecated tag
-                } else if (tag.equals(TAG_PROFILE_OWNER)) {
-                    String profileOwnerPackageName = parser.getAttributeValue(null, ATTR_PACKAGE);
-                    String profileOwnerName = parser.getAttributeValue(null, ATTR_NAME);
-                    String profileOwnerComponentStr =
-                            parser.getAttributeValue(null, ATTR_COMPONENT_NAME);
-                    int userId = parser.getAttributeInt(null, ATTR_USERID);
-                    OwnerInfo profileOwnerInfo = null;
-                    if (profileOwnerComponentStr != null) {
-                        ComponentName admin = ComponentName.unflattenFromString(
-                                profileOwnerComponentStr);
-                        if (admin != null) {
-                            profileOwnerInfo = new OwnerInfo(profileOwnerName, admin,
-                                    /* userRestrictionsMigrated =*/ false, null,
-                                    null, /* isOrganizationOwnedDevice =*/ false);
-                        } else {
-                            // This shouldn't happen but switch from package name -> component name
-                            // might have written bad device owner files. b/17652534
-                            Slog.e(TAG, "Error parsing device-owner file. Bad component name " +
-                                    profileOwnerComponentStr);
-                        }
-                    }
-                    if (profileOwnerInfo == null) {
-                        profileOwnerInfo = new OwnerInfo(profileOwnerName, profileOwnerPackageName,
-                                /* userRestrictionsMigrated =*/ false,
-                                /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/
-                                null, /* isOrganizationOwnedDevice =*/ false);
-                    }
-                    mProfileOwners.put(userId, profileOwnerInfo);
-                } else if (TAG_SYSTEM_UPDATE_POLICY.equals(tag)) {
-                    mSystemUpdatePolicy = SystemUpdatePolicy.restoreFromXml(parser);
-                } else {
-                    throw new XmlPullParserException(
-                            "Unexpected tag in device owner file: " + tag);
-                }
-            }
-            input.close();
-        } catch (XmlPullParserException | IOException e) {
-            Slog.e(TAG, "Error parsing device-owner file", e);
-        }
-        return true;
-    }
-
     void writeDeviceOwner() {
         synchronized (mLock) {
             if (DEBUG) {
@@ -1047,9 +901,6 @@
                             mDeviceOwnerUserId);
                     break;
                 }
-                case TAG_DEVICE_INITIALIZER:
-                    // Deprecated tag
-                    break;
                 case TAG_SYSTEM_UPDATE_POLICY:
                     mSystemUpdatePolicy = SystemUpdatePolicy.restoreFromXml(parser);
                     break;
@@ -1087,7 +938,6 @@
                 default:
                     Slog.e(TAG, "Unexpected tag: " + tag);
                     return false;
-
             }
             return true;
         }
@@ -1136,30 +986,16 @@
         public final String name;
         public final String packageName;
         public final ComponentName admin;
-        public boolean userRestrictionsMigrated;
         public String remoteBugreportUri;
         public String remoteBugreportHash;
         public boolean isOrganizationOwnedDevice;
 
-        public OwnerInfo(String name, String packageName, boolean userRestrictionsMigrated,
-                String remoteBugreportUri, String remoteBugreportHash,
-                boolean isOrganizationOwnedDevice) {
-            this.name = name;
-            this.packageName = packageName;
-            this.admin = new ComponentName(packageName, "");
-            this.userRestrictionsMigrated = userRestrictionsMigrated;
-            this.remoteBugreportUri = remoteBugreportUri;
-            this.remoteBugreportHash = remoteBugreportHash;
-            this.isOrganizationOwnedDevice = isOrganizationOwnedDevice;
-        }
-
-        public OwnerInfo(String name, ComponentName admin, boolean userRestrictionsMigrated,
+        OwnerInfo(String name, ComponentName admin,
                 String remoteBugreportUri, String remoteBugreportHash,
                 boolean isOrganizationOwnedDevice) {
             this.name = name;
             this.admin = admin;
             this.packageName = admin.getPackageName();
-            this.userRestrictionsMigrated = userRestrictionsMigrated;
             this.remoteBugreportUri = remoteBugreportUri;
             this.remoteBugreportHash = remoteBugreportHash;
             this.isOrganizationOwnedDevice = isOrganizationOwnedDevice;
@@ -1167,14 +1003,12 @@
 
         public void writeToXml(TypedXmlSerializer out, String tag) throws IOException {
             out.startTag(null, tag);
-            out.attribute(null, ATTR_PACKAGE, packageName);
             if (name != null) {
                 out.attribute(null, ATTR_NAME, name);
             }
             if (admin != null) {
                 out.attribute(null, ATTR_COMPONENT_NAME, admin.flattenToString());
             }
-            out.attributeBoolean(null, ATTR_USER_RESTRICTIONS_MIGRATED, userRestrictionsMigrated);
             if (remoteBugreportUri != null) {
                 out.attribute(null, ATTR_REMOTE_BUGREPORT_URI, remoteBugreportUri);
             }
@@ -1189,14 +1023,9 @@
         }
 
         public static OwnerInfo readFromXml(TypedXmlPullParser parser) {
-            final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
             final String name = parser.getAttributeValue(null, ATTR_NAME);
             final String componentName =
                     parser.getAttributeValue(null, ATTR_COMPONENT_NAME);
-            final String userRestrictionsMigratedStr =
-                    parser.getAttributeValue(null, ATTR_USER_RESTRICTIONS_MIGRATED);
-            final boolean userRestrictionsMigrated =
-                    ("true".equals(userRestrictionsMigratedStr));
             final String remoteBugreportUri = parser.getAttributeValue(null,
                     ATTR_REMOTE_BUGREPORT_URI);
             final String remoteBugreportHash = parser.getAttributeValue(null,
@@ -1210,23 +1039,18 @@
             final boolean isOrgOwnedDevice =
                     ("true".equals(isOrgOwnedDeviceStr)) | canAccessDeviceIds;
 
-            // Has component name?  If so, return [name, component]
-            if (componentName != null) {
-                final ComponentName admin = ComponentName.unflattenFromString(componentName);
-                if (admin != null) {
-                    return new OwnerInfo(name, admin, userRestrictionsMigrated,
-                            remoteBugreportUri, remoteBugreportHash, isOrgOwnedDevice);
-                } else {
-                    // This shouldn't happen but switch from package name -> component name
-                    // might have written bad device owner files. b/17652534
-                    Slog.e(TAG, "Error parsing owner file. Bad component name " +
-                            componentName);
-                }
+            if (componentName == null) {
+                Slog.e(TAG, "Owner component not found");
+                return null;
+            }
+            final ComponentName admin = ComponentName.unflattenFromString(componentName);
+            if (admin == null) {
+                Slog.e(TAG, "Owner component not parsable: " + componentName);
+                return null;
             }
 
-            // Else, build with [name, package]
-            return new OwnerInfo(name, packageName, userRestrictionsMigrated, remoteBugreportUri,
-                    remoteBugreportHash, isOrgOwnedDevice);
+            return new OwnerInfo(
+                        name, admin, remoteBugreportUri, remoteBugreportHash, isOrgOwnedDevice);
         }
 
         public void dump(IndentingPrintWriter pw) {
@@ -1284,11 +1108,6 @@
     }
 
     @VisibleForTesting
-    File getLegacyConfigFile() {
-        return new File(mInjector.environmentGetDataSystemDirectory(), DEVICE_OWNER_XML_LEGACY);
-    }
-
-    @VisibleForTesting
     File getDeviceOwnerFile() {
         return new File(mInjector.environmentGetDataSystemDirectory(), DEVICE_OWNER_XML);
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 6a27ecc..1753fc7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -737,6 +737,101 @@
         assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
     }
 
+    @Test
+    public void testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob_longPeriod() {
+        JobStatus job = createJobStatus(
+                "testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob_longPeriod",
+                createJobInfo().setPeriodic(7 * DAY_IN_MILLIS, 9 * HOUR_IN_MILLIS));
+        JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job);
+        // First window starts 6.625 days from now.
+        advanceElapsedClock(6 * DAY_IN_MILLIS + 15 * HOUR_IN_MILLIS);
+        long now = sElapsedRealtimeClock.millis();
+        long nextWindowStartTime = now + 7 * DAY_IN_MILLIS;
+        long nextWindowEndTime = nextWindowStartTime + 9 * HOUR_IN_MILLIS;
+
+        advanceElapsedClock(6 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS);
+        // Say the job ran at the very end of its previous window. The intended JSS behavior is to
+        // have consistent windows, so the new window should start as soon as the previous window
+        // ended and end PERIOD time after the previous window ended.
+        JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        advanceElapsedClock(DAY_IN_MILLIS);
+        // Say the job ran a day late. Since the period is massive compared to the flex, JSS should
+        // put the rescheduled job in the original window.
+        rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        // 1 day before the start of the next window. Given the large period, respect the original
+        // next window.
+        advanceElapsedClock(nextWindowStartTime - sElapsedRealtimeClock.millis() - DAY_IN_MILLIS);
+        rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        // 1 hour before the start of the next window. It's too close to the next window, so the
+        // returned job should be for the window after.
+        long oneHourBeforeNextWindow =
+                nextWindowStartTime - sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS;
+        long fiveMinsBeforeNextWindow =
+                nextWindowStartTime - sElapsedRealtimeClock.millis() - 5 * MINUTE_IN_MILLIS;
+        advanceElapsedClock(oneHourBeforeNextWindow);
+        nextWindowStartTime += 7 * DAY_IN_MILLIS;
+        nextWindowEndTime += 7 * DAY_IN_MILLIS;
+        rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        // 5 minutes before the start of the next window. It's too close to the next window, so the
+        // returned job should be for the window after.
+        advanceElapsedClock(fiveMinsBeforeNextWindow);
+        rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        advanceElapsedClock(14 * DAY_IN_MILLIS);
+        // Say that the job ran at this point, probably because the phone was off the entire time.
+        // The next window should be consistent (start and end at the time it would have had the job
+        // run normally in previous windows).
+        nextWindowStartTime += 14 * DAY_IN_MILLIS;
+        nextWindowEndTime += 14 * DAY_IN_MILLIS;
+
+        rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        // Test original job again but with a huge delay from the original execution window
+
+        // 1 day before the start of the next window. Given the large period, respect the original
+        // next window.
+        advanceElapsedClock(nextWindowStartTime - sElapsedRealtimeClock.millis() - DAY_IN_MILLIS);
+        rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        // 1 hour before the start of the next window. It's too close to the next window, so the
+        // returned job should be for the window after.
+        oneHourBeforeNextWindow =
+                nextWindowStartTime - sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS;
+        fiveMinsBeforeNextWindow =
+                nextWindowStartTime - sElapsedRealtimeClock.millis() - 5 * MINUTE_IN_MILLIS;
+        advanceElapsedClock(oneHourBeforeNextWindow);
+        nextWindowStartTime += 7 * DAY_IN_MILLIS;
+        nextWindowEndTime += 7 * DAY_IN_MILLIS;
+        rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+
+        // 5 minutes before the start of the next window. It's too close to the next window, so the
+        // returned job should be for the window after.
+        advanceElapsedClock(fiveMinsBeforeNextWindow);
+        rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+        assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+        assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+    }
+
     /** Tests that rare job batching works as expected. */
     @Test
     public void testRareJobBatching() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
index 9cf6c03..5c4657f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
@@ -67,6 +67,8 @@
 import com.android.server.power.batterysaver.BatterySavingStats;
 import com.android.server.testutils.OffsettableClock;
 
+import java.util.concurrent.Executor;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -164,7 +166,8 @@
             @Override
             Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
                     SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
-                    FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) {
+                    FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector,
+                    Executor executor) {
                 return mNotifierMock;
             }
 
diff --git a/services/tests/servicestests/assets/OwnersTest/device_owner_1.xml b/services/tests/servicestests/assets/OwnersTest/device_owner_1.xml
new file mode 100644
index 0000000..d150b29
--- /dev/null
+++ b/services/tests/servicestests/assets/OwnersTest/device_owner_1.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<root>
+    <device-owner package="com.afwsamples.testdpc" name="" component="com.afwsamples.testdpc/com.afwsamples.testdpc.DeviceAdminReceiver" isPoOrganizationOwnedDevice="true" />
+    <device-owner-context userId="0" />
+    <system-update-policy policy_type="2" install_window_start="0" install_window_end="540" />
+</root>
\ No newline at end of file
diff --git a/services/tests/servicestests/assets/OwnersTest/profile_owner_1.xml b/services/tests/servicestests/assets/OwnersTest/profile_owner_1.xml
new file mode 100644
index 0000000..1f78b71
--- /dev/null
+++ b/services/tests/servicestests/assets/OwnersTest/profile_owner_1.xml
@@ -0,0 +1,4 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<root>
+    <profile-owner package="com.afwsamples.testdpc" name="com.afwsamples.testdpc" component="com.afwsamples.testdpc/com.afwsamples.testdpc.DeviceAdminReceiver" />
+</root>
\ No newline at end of file
diff --git a/services/tests/servicestests/assets/OwnersTest/test01/input.xml b/services/tests/servicestests/assets/OwnersTest/test01/input.xml
deleted file mode 100644
index db3e974..0000000
--- a/services/tests/servicestests/assets/OwnersTest/test01/input.xml
+++ /dev/null
@@ -1 +0,0 @@
-<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
diff --git a/services/tests/servicestests/assets/OwnersTest/test02/input.xml b/services/tests/servicestests/assets/OwnersTest/test02/input.xml
deleted file mode 100644
index 321842b..0000000
--- a/services/tests/servicestests/assets/OwnersTest/test02/input.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
-<device-owner package="com.google.android.testdpc" />
diff --git a/services/tests/servicestests/assets/OwnersTest/test03/input.xml b/services/tests/servicestests/assets/OwnersTest/test03/input.xml
deleted file mode 100644
index 1bbfdadf..0000000
--- a/services/tests/servicestests/assets/OwnersTest/test03/input.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
-<profile-owner package="com.google.android.testdpc0" name="0" userId="10" component="com.google.android.testdpc/com.google.android.testdpc.DeviceAdminReceiver0" />
-<profile-owner package="com.google.android.testdpc1" name="1" userId="11" />
diff --git a/services/tests/servicestests/assets/OwnersTest/test04/input.xml b/services/tests/servicestests/assets/OwnersTest/test04/input.xml
deleted file mode 100644
index 8be51d9..0000000
--- a/services/tests/servicestests/assets/OwnersTest/test04/input.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
-<device-owner package="com.google.android.testdpc" />
-<profile-owner package="com.google.android.testdpc0" name="0" userId="10" component="com.google.android.testdpc/com.google.android.testdpc.DeviceAdminReceiver0" />
-<profile-owner package="com.google.android.testdpc1" name="1" userId="11" />
-<device-initializer package="com.google.android.testdpcx" name="di" component="com.google.android.testdpcx/receiver" />
-<system-update-policy policy_type="5" />
diff --git a/services/tests/servicestests/assets/OwnersTest/test05/input.xml b/services/tests/servicestests/assets/OwnersTest/test05/input.xml
deleted file mode 100644
index dbcb858..0000000
--- a/services/tests/servicestests/assets/OwnersTest/test05/input.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
-<device-initializer package="com.google.android.testdpcx" name="di" component="com.google.android.testdpcx/receiver" />
-
diff --git a/services/tests/servicestests/assets/OwnersTest/test06/input.xml b/services/tests/servicestests/assets/OwnersTest/test06/input.xml
deleted file mode 100644
index 794622b..0000000
--- a/services/tests/servicestests/assets/OwnersTest/test06/input.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
-<system-update-policy policy_type="5" />
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index 3d3c1ab..4e059b4 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -16,6 +16,9 @@
 
 package com.android.server;
 
+import static com.android.server.GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
+import static com.android.server.GestureLauncherService.EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -248,7 +251,7 @@
         mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
 
         long eventTime = INITIAL_EVENT_TIME_MILLIS +
-                GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+                CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
         boolean interactive = true;
@@ -296,7 +299,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
@@ -341,7 +344,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
+        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
@@ -435,7 +438,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
@@ -489,7 +492,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
 
         // 2nd button triggers camera
         eventTime += interval;
@@ -578,7 +581,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         // 3 more button presses which should not trigger any gesture (camera gesture disabled)
         for (int i = 0; i < 3; i++) {
             eventTime += interval;
@@ -632,7 +635,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         // 3 more button presses which should not trigger any gesture, but intercepts action.
         for (int i = 0; i < 3; i++) {
             eventTime += interval;
@@ -735,7 +738,7 @@
                     interactive, outLaunched);
             assertTrue(intercepted);
             assertFalse(outLaunched.value);
-            interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+            interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
             eventTime += interval;
         }
     }
@@ -763,12 +766,33 @@
                     interactive, outLaunched);
             assertTrue(intercepted);
             assertFalse(outLaunched.value);
-            interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+            interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
             eventTime += interval;
         }
     }
 
     @Test
+    public void testInterceptPowerKeyDown_triggerEmergency_fiveFastTaps_gestureIgnored() {
+        // Trigger emergency by tapping button 5 times
+        long eventTime = triggerEmergencyGesture(/* tapIntervalMs= */ 1);
+
+        // Add 1 more millisecond and send the event again.
+        eventTime += 1;
+
+        // Subsequent long press is intercepted, but should not trigger any gesture
+        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+                IGNORED_REPEAT, IGNORED_META_STATE, IGNORED_DEVICE_ID, IGNORED_SCANCODE,
+                KeyEvent.FLAG_LONG_PRESS);
+        MutableBoolean outLaunched = new MutableBoolean(true);
+        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(
+                keyEvent,
+                /* interactive= */ true,
+                outLaunched);
+        assertFalse(intercepted);
+        assertFalse(outLaunched.value);
+    }
+
+    @Test
     public void testInterceptPowerKeyDown_triggerEmergency_longPress_cooldownTriggered() {
         // Enable power button cooldown
         withEmergencyGesturePowerButtonCooldownPeriodMsValue(3000);
@@ -890,7 +914,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT, IGNORED_META_STATE, IGNORED_DEVICE_ID, IGNORED_SCANCODE,
@@ -936,7 +960,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
@@ -983,7 +1007,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
+        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
@@ -1075,7 +1099,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
@@ -1120,7 +1144,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
+        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
@@ -1212,7 +1236,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
@@ -1261,7 +1285,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
@@ -1306,7 +1330,7 @@
         assertFalse(intercepted);
         assertFalse(outLaunched.value);
 
-        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
+        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
         eventTime += interval;
         keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                 IGNORED_REPEAT);
@@ -1384,9 +1408,20 @@
 
     /**
      * Helper method to trigger emergency gesture by pressing button for 5 times.
+     *
      * @return last event time.
      */
     private long triggerEmergencyGesture() {
+        return triggerEmergencyGesture(CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1);
+    }
+
+    /**
+     * Helper method to trigger emergency gesture by pressing button for 5 times with
+     * specified interval between each tap
+     *
+     * @return last event time.
+     */
+    private long triggerEmergencyGesture(long tapIntervalMs) {
         // Enable emergency power gesture
         withEmergencyGestureEnabledConfigValue(true);
         withEmergencyGestureEnabledSettingValue(true);
@@ -1402,8 +1437,7 @@
             keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
                     IGNORED_REPEAT);
             mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, outLaunched);
-            final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
-            eventTime += interval;
+            eventTime += tapIntervalMs;
         }
 
         // 5th button press should trigger the emergency flow
@@ -1412,12 +1446,22 @@
         outLaunched.value = false;
         boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
                 outLaunched);
-        assertTrue(outLaunched.value);
+        long emergencyGestureTapDetectionMinTimeMs = Settings.Global.getInt(
+                mContext.getContentResolver(),
+                Settings.Global.EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS,
+                EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS);
         assertTrue(intercepted);
-        verify(mUiEventLogger, times(1))
-                .log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER);
-        verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected();
-
+        if (tapIntervalMs * 4 > emergencyGestureTapDetectionMinTimeMs) {
+            assertTrue(outLaunched.value);
+            verify(mUiEventLogger, times(1))
+                    .log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER);
+            verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected();
+        } else {
+            assertFalse(outLaunched.value);
+            verify(mUiEventLogger, never())
+                    .log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER);
+            verify(mStatusBarManagerInternal, never()).onEmergencyActionLaunchGestureDetected();
+        }
         return eventTime;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 97ebdd4..08df438 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -58,6 +58,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -435,7 +436,7 @@
                 VIEWID_RESOURCE_NAME, INTERACTION_ID, mMockCallback, TID);
         verify(mMockIA11yInteractionConnection).findAccessibilityNodeInfosByViewId(
                 eq(ROOT_NODE_ID), eq(VIEWID_RESOURCE_NAME), any(), eq(INTERACTION_ID),
-                captor.capture(), anyInt(), eq(PID), eq(TID), any());
+                captor.capture(), anyInt(), eq(PID), eq(TID), any(), nullable(float[].class));
         verify(mMockSecurityPolicy).computeValidReportedPackages(any(), anyInt());
         verifyReplaceActions(captor.getValue());
     }
@@ -449,7 +450,7 @@
                 VIEW_TEXT, INTERACTION_ID, mMockCallback, TID);
         verify(mMockIA11yInteractionConnection).findAccessibilityNodeInfosByText(
                 eq(ROOT_NODE_ID), eq(VIEW_TEXT), any(), eq(INTERACTION_ID),
-                captor.capture(), anyInt(), eq(PID), eq(TID), any());
+                captor.capture(), anyInt(), eq(PID), eq(TID), any(), nullable(float[].class));
         verify(mMockSecurityPolicy).computeValidReportedPackages(any(), anyInt());
         verifyReplaceActions(captor.getValue());
     }
@@ -463,7 +464,7 @@
                 INTERACTION_ID, mMockCallback, 0, TID, null);
         verify(mMockIA11yInteractionConnection).findAccessibilityNodeInfoByAccessibilityId(
                 eq(ROOT_NODE_ID), any(), eq(INTERACTION_ID), captor.capture(), anyInt(),
-                eq(PID), eq(TID), any(), any());
+                eq(PID), eq(TID), any(),  nullable(float[].class), any());
         verify(mMockSecurityPolicy).computeValidReportedPackages(any(), anyInt());
         verifyReplaceActions(captor.getValue());
     }
@@ -476,7 +477,8 @@
         mServiceConnection.findFocus(PIP_WINDOWID, ROOT_NODE_ID, FOCUS_INPUT, INTERACTION_ID,
                 mMockCallback, TID);
         verify(mMockIA11yInteractionConnection).findFocus(eq(ROOT_NODE_ID), eq(FOCUS_INPUT),
-                any(), eq(INTERACTION_ID), captor.capture(), anyInt(), eq(PID), eq(TID), any());
+                any(), eq(INTERACTION_ID), captor.capture(), anyInt(), eq(PID), eq(TID), any(),
+                nullable(float[].class));
         verify(mMockSecurityPolicy).computeValidReportedPackages(any(), anyInt());
         verifyReplaceActions(captor.getValue());
     }
@@ -489,7 +491,8 @@
         mServiceConnection.focusSearch(PIP_WINDOWID, ROOT_NODE_ID, FOCUS_DOWN, INTERACTION_ID,
                 mMockCallback, TID);
         verify(mMockIA11yInteractionConnection).focusSearch(eq(ROOT_NODE_ID), eq(FOCUS_DOWN),
-                any(), eq(INTERACTION_ID), captor.capture(), anyInt(), eq(PID), eq(TID), any());
+                any(), eq(INTERACTION_ID), captor.capture(), anyInt(), eq(PID), eq(TID), any(),
+                nullable(float[].class));
         verify(mMockSecurityPolicy).computeValidReportedPackages(any(), anyInt());
         verifyReplaceActions(captor.getValue());
     }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java
index 022c137..842b23c 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInteractionControllerNodeRequestsTest.java
@@ -842,7 +842,7 @@
                 null, interactionId,
                 callback, prefetchFlags,
                 processAndThreadId,
-                processAndThreadId, null, null);
+                processAndThreadId, null, null, null);
 
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java b/services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java
index 72820f1..c16f3d3f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/ActionReplacingCallbackTest.java
@@ -16,6 +16,27 @@
 
 package com.android.server.accessibility;
 
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS;
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK;
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE;
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CONTEXT_CLICK;
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND;
+
+import static junit.framework.TestCase.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.MockitoAnnotations.initMocks;
+
 import android.graphics.Region;
 import android.os.RemoteException;
 import android.view.MagnificationSpec;
@@ -35,28 +56,8 @@
 import org.mockito.Mock;
 
 import java.util.Arrays;
-import java.util.HashSet;
 import java.util.List;
 
-import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS;
-import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
-import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK;
-import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE;
-import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND;
-import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CONTEXT_CLICK;
-import static junit.framework.TestCase.assertTrue;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.MockitoAnnotations.initMocks;
-
 /**
  * Tests for ActionReplacingCallback
  */
@@ -130,8 +131,8 @@
         verify(mMockReplacerConnection).findAccessibilityNodeInfoByAccessibilityId(
                 eq(AccessibilityNodeInfo.ROOT_NODE_ID), (Region) anyObject(),
                 mInteractionIdCaptor.capture(), eq(mActionReplacingCallback), eq(0),
-                eq(INTERROGATING_PID), eq(INTERROGATING_TID), (MagnificationSpec) anyObject(),
-                eq(null));
+                eq(INTERROGATING_PID), eq(INTERROGATING_TID), nullable(MagnificationSpec.class),
+                nullable(float[].class), eq(null));
         mReplacerInteractionId = mInteractionIdCaptor.getValue().intValue();
     }
 
@@ -247,7 +248,7 @@
                 .findAccessibilityNodeInfoByAccessibilityId(eq(AccessibilityNodeInfo.ROOT_NODE_ID),
                         (Region) anyObject(), anyInt(), (ActionReplacingCallback) anyObject(),
                         eq(0),  eq(INTERROGATING_PID), eq(INTERROGATING_TID),
-                        (MagnificationSpec) anyObject(), eq(null));
+                        (MagnificationSpec) anyObject(), nullable(float[].class), eq(null));
         ActionReplacingCallback actionReplacingCallback = new ActionReplacingCallback(
                 mMockServiceCallback, mMockReplacerConnection, INTERACTION_ID, INTERROGATING_PID,
                 INTERROGATING_TID);
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index e4f1a96..db12092 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -28,6 +28,7 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.server.am.UserController.CLEAR_USER_JOURNEY_SESSION_MSG;
 import static com.android.server.am.UserController.COMPLETE_USER_SWITCH_MSG;
 import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG;
 import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
@@ -410,6 +411,7 @@
         expectedCodes.add(COMPLETE_USER_SWITCH_MSG);
         expectedCodes.add(REPORT_USER_SWITCH_COMPLETE_MSG);
         if (backgroundUserStopping) {
+            expectedCodes.add(CLEAR_USER_JOURNEY_SESSION_MSG);
             expectedCodes.add(0); // this is for directly posting in stopping.
         }
         Set<Integer> actualCodes = mInjector.mHandler.getMessageCodes();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index f834b34..8bb619f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -27,19 +27,15 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.admin.DevicePolicyManager;
-import android.app.admin.DevicePolicyManagerInternal;
-import android.app.admin.DevicePolicyManagerLiteInternal;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.Build;
-import android.os.Bundle;
 import android.os.IpcDataCache;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -50,7 +46,6 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.servicestests.R;
-import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
 import org.junit.Before;
@@ -58,8 +53,6 @@
 import org.junit.runner.RunWith;
 
 import java.io.File;
-import java.util.HashMap;
-import java.util.Map;
 import java.util.Set;
 
 @Presubmit
@@ -90,232 +83,9 @@
                 .thenReturn(true);
     }
 
-    @Test
-    public void testMigration() throws Exception {
-        final File user10dir = getServices().addUser(10, 0, USER_TYPE_EMPTY);
-        final File user11dir = getServices().addUser(11, 0,
-                UserManager.USER_TYPE_PROFILE_MANAGED);
-        getServices().addUser(12, 0, USER_TYPE_EMPTY);
-
-        setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
-        setUpPackageManagerForAdmin(admin2, UserHandle.getUid(10, 123));
-        setUpPackageManagerForAdmin(admin3, UserHandle.getUid(11, 456));
-
-        // Create the legacy owners & policies file.
-        DpmTestUtils.writeToFile(
-                (new File(getServices().dataDir, "device_owner.xml")).getAbsoluteFile(),
-                DpmTestUtils.readAsset(mRealTestContext,
-                        "DevicePolicyManagerServiceMigrationTest/legacy_device_owner.xml"));
-
-        DpmTestUtils.writeToFile(
-                (new File(getServices().systemUserDataDir, "device_policies.xml")).getAbsoluteFile(),
-                DpmTestUtils.readAsset(mRealTestContext,
-                        "DevicePolicyManagerServiceMigrationTest/legacy_device_policies.xml"));
-
-        DpmTestUtils.writeToFile(
-                (new File(user10dir, "device_policies.xml")).getAbsoluteFile(),
-                DpmTestUtils.readAsset(mRealTestContext,
-                        "DevicePolicyManagerServiceMigrationTest/legacy_device_policies_10.xml"));
-        DpmTestUtils.writeToFile(
-                (new File(user11dir, "device_policies.xml")).getAbsoluteFile(),
-                DpmTestUtils.readAsset(mRealTestContext,
-                        "DevicePolicyManagerServiceMigrationTest/legacy_device_policies_11.xml"));
-
-        // Set up UserManager
-        when(getServices().userManagerInternal.getBaseUserRestrictions(
-                eq(USER_SYSTEM))).thenReturn(DpmTestUtils.newRestrictions(
-                UserManager.DISALLOW_ADD_USER,
-                UserManager.DISALLOW_RECORD_AUDIO));
-
-        when(getServices().userManagerInternal.getBaseUserRestrictions(
-                eq(10))).thenReturn(DpmTestUtils.newRestrictions(
-                UserManager.DISALLOW_REMOVE_USER,
-                UserManager.DISALLOW_ADD_USER,
-                UserManager.DISALLOW_SMS,
-                UserManager.DISALLOW_OUTGOING_CALLS,
-                UserManager.DISALLOW_WALLPAPER,
-                UserManager.DISALLOW_RECORD_AUDIO));
-
-        when(getServices().userManagerInternal.getBaseUserRestrictions(
-                eq(11))).thenReturn(DpmTestUtils.newRestrictions(
-                UserManager.DISALLOW_REMOVE_USER,
-                UserManager.DISALLOW_ADD_USER,
-                UserManager.DISALLOW_SMS,
-                UserManager.DISALLOW_OUTGOING_CALLS,
-                UserManager.DISALLOW_WALLPAPER,
-                UserManager.DISALLOW_RECORD_AUDIO));
-
-        final Map<Integer, Bundle> newBaseRestrictions = new HashMap<>();
-
-        doAnswer(invocation -> {
-            Integer userId = (Integer) invocation.getArguments()[0];
-            Bundle bundle = (Bundle) invocation.getArguments()[1];
-
-            newBaseRestrictions.put(userId, bundle);
-
-            return null;
-        }).when(getServices().userManagerInternal).setBaseUserRestrictionsByDpmsForMigration(
-                anyInt(), any(Bundle.class));
-
-        // Initialize DPM/DPMS and let it migrate the persisted information.
-        // (Need clearCallingIdentity() to pass permission checks.)
-
-        final DevicePolicyManagerServiceTestable dpms;
-
-        final long ident = mContext.binder.clearCallingIdentity();
-        try {
-            LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
-            LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
-
-            dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
-
-            dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY);
-            dpms.systemReady(SystemService.PHASE_BOOT_COMPLETED);
-        } finally {
-            mContext.binder.restoreCallingIdentity(ident);
-        }
-
-        assertThat(dpms.mOwners.hasDeviceOwner()).isTrue();
-        assertThat(dpms.mOwners.hasProfileOwner(USER_SYSTEM)).isFalse();
-        assertThat(dpms.mOwners.hasProfileOwner(10)).isTrue();
-        assertThat(dpms.mOwners.hasProfileOwner(11)).isTrue();
-        assertThat(dpms.mOwners.hasProfileOwner(12)).isFalse();
-
-        // Now all information should be migrated.
-        assertThat(dpms.mOwners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse();
-        assertThat(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(USER_SYSTEM))
-            .isFalse();
-        assertThat(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isFalse();
-        assertThat(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse();
-        assertThat(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(12)).isFalse();
-
-        // Check the new base restrictions.
-        DpmTestUtils.assertRestrictions(
-                DpmTestUtils.newRestrictions(
-                        UserManager.DISALLOW_RECORD_AUDIO
-                ),
-                newBaseRestrictions.get(USER_SYSTEM));
-
-        DpmTestUtils.assertRestrictions(
-                DpmTestUtils.newRestrictions(
-                        UserManager.DISALLOW_ADD_USER,
-                        UserManager.DISALLOW_SMS,
-                        UserManager.DISALLOW_OUTGOING_CALLS,
-                        UserManager.DISALLOW_RECORD_AUDIO,
-                        UserManager.DISALLOW_WALLPAPER
-                ),
-                newBaseRestrictions.get(10));
-
-        DpmTestUtils.assertRestrictions(
-                DpmTestUtils.newRestrictions(
-                        UserManager.DISALLOW_ADD_USER,
-                        UserManager.DISALLOW_SMS,
-                        UserManager.DISALLOW_OUTGOING_CALLS,
-                        UserManager.DISALLOW_WALLPAPER,
-                        UserManager.DISALLOW_RECORD_AUDIO
-                ),
-                newBaseRestrictions.get(11));
-
-        // Check the new owner restrictions.
-        DpmTestUtils.assertRestrictions(
-                DpmTestUtils.newRestrictions(
-                        UserManager.DISALLOW_ADD_USER
-                ),
-                dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions());
-
-        DpmTestUtils.assertRestrictions(
-                DpmTestUtils.newRestrictions(
-                        UserManager.DISALLOW_REMOVE_USER
-                ),
-                dpms.getProfileOwnerAdminLocked(10).ensureUserRestrictions());
-
-        DpmTestUtils.assertRestrictions(
-                DpmTestUtils.newRestrictions(
-                        UserManager.DISALLOW_REMOVE_USER
-                ),
-                dpms.getProfileOwnerAdminLocked(11).ensureUserRestrictions());
-    }
-
-    @Test
-    public void testMigration2_profileOwnerOnUser0() throws Exception {
-        setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_SYSTEM_USER_UID);
-
-        // Create the legacy owners & policies file.
-        DpmTestUtils.writeToFile(
-                (new File(getServices().dataDir, "device_owner.xml")).getAbsoluteFile(),
-                DpmTestUtils.readAsset(mRealTestContext,
-                        "DevicePolicyManagerServiceMigrationTest2/legacy_device_owner.xml"));
-
-        DpmTestUtils.writeToFile(
-                (new File(getServices().systemUserDataDir, "device_policies.xml")).getAbsoluteFile(),
-                DpmTestUtils.readAsset(mRealTestContext,
-                        "DevicePolicyManagerServiceMigrationTest2/legacy_device_policies.xml"));
-
-        // Set up UserManager
-        when(getServices().userManagerInternal.getBaseUserRestrictions(
-                eq(USER_SYSTEM))).thenReturn(DpmTestUtils.newRestrictions(
-                UserManager.DISALLOW_ADD_USER,
-                UserManager.DISALLOW_RECORD_AUDIO,
-                UserManager.DISALLOW_SMS,
-                UserManager.DISALLOW_OUTGOING_CALLS));
-
-        final Map<Integer, Bundle> newBaseRestrictions = new HashMap<>();
-
-        doAnswer(invocation -> {
-            Integer userId = (Integer) invocation.getArguments()[0];
-            Bundle bundle = (Bundle) invocation.getArguments()[1];
-
-            newBaseRestrictions.put(userId, bundle);
-
-            return null;
-        }).when(getServices().userManagerInternal).setBaseUserRestrictionsByDpmsForMigration(
-                anyInt(), any(Bundle.class));
-
-        // Initialize DPM/DPMS and let it migrate the persisted information.
-        // (Need clearCallingIdentity() to pass permission checks.)
-
-        final DevicePolicyManagerServiceTestable dpms;
-
-        final long ident = mContext.binder.clearCallingIdentity();
-        try {
-            LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
-            LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
-
-            dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
-
-            dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY);
-            dpms.systemReady(SystemService.PHASE_BOOT_COMPLETED);
-        } finally {
-            mContext.binder.restoreCallingIdentity(ident);
-        }
-        assertThat(dpms.mOwners.hasDeviceOwner()).isFalse();
-        assertThat(dpms.mOwners.hasProfileOwner(USER_SYSTEM)).isTrue();
-
-        // Now all information should be migrated.
-        assertThat(dpms.mOwners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse();
-        assertThat(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(USER_SYSTEM))
-            .isFalse();
-
-        // Check the new base restrictions.
-        DpmTestUtils.assertRestrictions(
-                DpmTestUtils.newRestrictions(
-                        UserManager.DISALLOW_RECORD_AUDIO
-                ),
-                newBaseRestrictions.get(USER_SYSTEM));
-
-        // Check the new owner restrictions.
-        DpmTestUtils.assertRestrictions(
-                DpmTestUtils.newRestrictions(
-                        UserManager.DISALLOW_ADD_USER,
-                        UserManager.DISALLOW_SMS,
-                        UserManager.DISALLOW_OUTGOING_CALLS
-                ),
-                dpms.getProfileOwnerAdminLocked(USER_SYSTEM).ensureUserRestrictions());
-    }
-
     // Test setting default restrictions for managed profile.
     @Test
-    public void testMigration3_managedProfileOwner() throws Exception {
+    public void testMigration_managedProfileOwner() throws Exception {
         // Create a managed profile user.
         final File user10dir = getServices().addUser(10, 0,
                 UserManager.USER_TYPE_PROFILE_MANAGED);
@@ -347,9 +117,6 @@
         // (Need clearCallingIdentity() to pass permission checks.)
         final long ident = mContext.binder.clearCallingIdentity();
         try {
-            LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
-            LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
-
             dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
 
             dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY);
@@ -508,9 +275,6 @@
         DevicePolicyManagerServiceTestable dpms;
         final long ident = mContext.binder.clearCallingIdentity();
         try {
-            LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
-            LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
-
             dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
 
             dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 4f87f9d..2dbf728 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -21,6 +21,8 @@
 import android.app.IActivityTaskManager;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DevicePolicyManagerLiteInternal;
 import android.app.backup.IBackupManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.Context;
@@ -49,6 +51,7 @@
 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockSettingsInternal;
+import com.android.server.LocalServices;
 import com.android.server.PersistentDataBlockManagerInternal;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.pm.UserManagerInternal;
@@ -99,11 +102,22 @@
     }
 
     private DevicePolicyManagerServiceTestable(MockInjector injector) {
-        super(injector);
+        super(unregisterLocalServices(injector));
         mMockInjector = injector;
         this.context = injector.context;
     }
 
+    /**
+     * Unregisters local services to avoid IllegalStateException when DPMS ctor re-registers them.
+     * This is made into a static method to circumvent the requirement to call super() first.
+     * Returns its parameter as is.
+     */
+    private static MockInjector unregisterLocalServices(MockInjector injector) {
+        LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
+        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+        return injector;
+    }
+
     public void notifyChangeToContentObserver(Uri uri, int userHandle) {
         ContentObserver co = mMockInjector.mContentObservers.get(new Pair<>(uri, userHandle));
         if (co != null) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index ea136da..45d101a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -328,8 +328,6 @@
     private void initializeDpms() {
         // Need clearCallingIdentity() to pass permission checks.
         final long ident = mContext.binder.clearCallingIdentity();
-        LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
-        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
 
         dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
         dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY);
@@ -417,8 +415,6 @@
         when(getServices().packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN)))
                 .thenReturn(false);
 
-        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
-        LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
         new DevicePolicyManagerServiceTestable(getServices(), mContext);
 
         // If the device has no DPMS feature, it shouldn't register the local service.
@@ -1669,39 +1665,6 @@
         assertThat(deviceOwner.getUid()).isEqualTo(DpmMockContext.CALLER_SYSTEM_USER_UID);
     }
 
-    /**
-     * This essentially tests
-     * {@code DevicePolicyManagerService.findOwnerComponentIfNecessaryLocked()}. (which is
-     * private.)
-     *
-     * We didn't use to persist the DO component class name, but now we do, and the above method
-     * finds the right component from a package name upon migration.
-     */
-    @Test
-    public void testDeviceOwnerMigration() throws Exception {
-        checkDeviceOwnerWithMultipleDeviceAdmins();
-
-        // Overwrite the device owner setting and clears the class name.
-        dpms.mOwners.setDeviceOwner(
-                new ComponentName(admin2.getPackageName(), ""),
-                "owner-name", CALLER_USER_HANDLE);
-        dpms.mOwners.writeDeviceOwner();
-
-        // Make sure the DO component name doesn't have a class name.
-        assertThat(dpms.getDeviceOwnerComponent(/* callingUserOnly= */ false).getClassName())
-                .isEmpty();
-
-        // Then create a new DPMS to have it load the settings from files.
-        when(getServices().userManager.getUserRestrictions(any(UserHandle.class)))
-                .thenReturn(new Bundle());
-        initializeDpms();
-
-        // Now the DO component name is a full name.
-        // *BUT* because both admin1 and admin2 belong to the same package, we think admin1 is the
-        // DO.
-        assertThat(dpms.getDeviceOwnerComponent(/* callingUserOnly =*/ false)).isEqualTo(admin1);
-    }
-
     @Test
     public void testSetGetApplicationRestriction() {
         setAsProfileOwner(admin1);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java
index 69aaf01..d55f379 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java
@@ -27,7 +27,6 @@
 
 import android.app.admin.ConnectEvent;
 import android.app.admin.DeviceAdminReceiver;
-import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.DnsEvent;
 import android.app.admin.NetworkEvent;
 import android.content.Intent;
@@ -40,8 +39,6 @@
 import android.os.test.TestLooper;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.server.LocalServices;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -66,7 +63,6 @@
                 android.Manifest.permission.MANAGE_DEVICE_ADMINS);
         doNothing().when(mSpiedDpmMockContext).sendBroadcastAsUser(any(Intent.class),
                 any(UserHandle.class));
-        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
         mDpmTestable = new DevicePolicyManagerServiceTestable(getServices(), mSpiedDpmMockContext);
         setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
         mDpmTestable.setActiveAdmin(admin1, true, DpmMockContext.CALLER_USER_HANDLE);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
index d1706f8..37ba8a4 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
@@ -18,29 +18,22 @@
 
 import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
 import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
+import static android.app.admin.SystemUpdatePolicy.TYPE_INSTALL_WINDOWED;
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Mockito.verify;
-
 import android.content.ComponentName;
 import android.os.IpcDataCache;
-import android.os.UserHandle;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.devicepolicy.DevicePolicyManagerServiceTestable.OwnersTestable;
 
-import com.google.android.collect.Lists;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * Tests for the DeviceOwner object that saves & loads device and policy owner information.
  *
@@ -52,8 +45,7 @@
 @RunWith(AndroidJUnit4.class)
 public class OwnersTest extends DpmTestBase {
 
-    private static final List<String> DEVICE_OWNER_PROTECTED_PACKAGES =
-            Lists.newArrayList("package_1", "package_2");
+    private static final String TESTDPC_PACKAGE = "com.afwsamples.testdpc";
 
     @Before
     public void setUp() throws Exception {
@@ -63,572 +55,66 @@
     }
 
     @Test
-    public void testUpgrade01() throws Exception {
-        getServices().addUsers(10, 11, 20, 21);
-
-        // First, migrate.
-        {
-            final OwnersTestable owners = new OwnersTestable(getServices());
-
-            DpmTestUtils.writeToFile(owners.getLegacyConfigFile(),
-                    DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test01/input.xml"));
-
-            owners.load();
-
-            // The legacy file should be removed.
-            assertThat(owners.getLegacyConfigFile().exists()).isFalse();
-
-            // File was empty, so no new files should be created.
-            assertThat(owners.getDeviceOwnerFile().exists()).isFalse();
-
-            assertThat(owners.getProfileOwnerFile(10).exists()).isFalse();
-            assertThat(owners.getProfileOwnerFile(11).exists()).isFalse();
-            assertThat(owners.getProfileOwnerFile(20).exists()).isFalse();
-            assertThat(owners.getProfileOwnerFile(21).exists()).isFalse();
-
-            assertThat(owners.hasDeviceOwner()).isFalse();
-            assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL);
-            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
-                    DEVICE_OWNER_TYPE_DEFAULT);
-            assertThat(owners.getDeviceOwnerProtectedPackages(owners.getDeviceOwnerPackageName()))
-                    .isEmpty();
-            assertThat(owners.getSystemUpdatePolicy()).isNull();
-            assertThat(owners.getProfileOwnerKeys()).isEmpty();
-
-            assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse();
-
-            owners.setDeviceOwnerType(owners.getDeviceOwnerPackageName(),
-                    DEVICE_OWNER_TYPE_FINANCED, /* isAdminTestOnly= */ false);
-            // There is no device owner, so the default owner type should be returned.
-            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
-                    DEVICE_OWNER_TYPE_DEFAULT);
-        }
-
-        // Then re-read and check.
-        {
-            final OwnersTestable owners = new OwnersTestable(getServices());
-            owners.load();
-
-            assertThat(owners.hasDeviceOwner()).isFalse();
-            assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL);
-            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
-                    DEVICE_OWNER_TYPE_DEFAULT);
-            assertThat(owners.getDeviceOwnerProtectedPackages(owners.getDeviceOwnerPackageName()))
-                    .isEmpty();
-            assertThat(owners.getSystemUpdatePolicy()).isNull();
-            assertThat(owners.getProfileOwnerKeys()).isEmpty();
-
-            assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse();
-        }
-    }
-
-    @Test
-    public void testUpgrade02() throws Exception {
-        getServices().addUsers(10, 11, 20, 21);
-
-        // First, migrate.
-        {
-            final OwnersTestable owners = new OwnersTestable(getServices());
-
-            DpmTestUtils.writeToFile(owners.getLegacyConfigFile(),
-                    DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test02/input.xml"));
-
-            owners.load();
-
-            // The legacy file should be removed.
-            assertThat(owners.getLegacyConfigFile().exists()).isFalse();
-
-            assertThat(owners.getDeviceOwnerFile().exists()).isTrue(); // TODO Check content
-
-            assertThat(owners.getProfileOwnerFile(10).exists()).isFalse();
-            assertThat(owners.getProfileOwnerFile(11).exists()).isFalse();
-            assertThat(owners.getProfileOwnerFile(20).exists()).isFalse();
-            assertThat(owners.getProfileOwnerFile(21).exists()).isFalse();
-
-            assertThat(owners.hasDeviceOwner()).isTrue();
-            assertThat(owners.getDeviceOwnerName()).isEqualTo(null);
-            assertThat(owners.getDeviceOwnerPackageName()).isEqualTo("com.google.android.testdpc");
-            assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM);
-            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
-                    DEVICE_OWNER_TYPE_DEFAULT);
-            assertThat(owners.getDeviceOwnerProtectedPackages(owners.getDeviceOwnerPackageName()))
-                    .isEmpty();
-
-            assertThat(owners.getSystemUpdatePolicy()).isNull();
-            assertThat(owners.getProfileOwnerKeys()).isEmpty();
-
-            assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isTrue();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse();
-        }
-
-        // Then re-read and check.
-        {
-            final OwnersTestable owners = new OwnersTestable(getServices());
-            owners.load();
-
-            assertThat(owners.hasDeviceOwner()).isTrue();
-            assertThat(owners.getDeviceOwnerName()).isEqualTo(null);
-            assertThat(owners.getDeviceOwnerPackageName()).isEqualTo("com.google.android.testdpc");
-            assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM);
-            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
-                    DEVICE_OWNER_TYPE_DEFAULT);
-            assertThat(owners.getDeviceOwnerProtectedPackages(owners.getDeviceOwnerPackageName()))
-                    .isEmpty();
-
-            assertThat(owners.getSystemUpdatePolicy()).isNull();
-            assertThat(owners.getProfileOwnerKeys()).isEmpty();
-
-            assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isTrue();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse();
-        }
-    }
-
-    @Test
-    public void testUpgrade03() throws Exception {
-        getServices().addUsers(10, 11, 20, 21);
-
-        // First, migrate.
-        {
-            final OwnersTestable owners = new OwnersTestable(getServices());
-
-            DpmTestUtils.writeToFile(owners.getLegacyConfigFile(),
-                    DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test03/input.xml"));
-
-            owners.load();
-
-            // The legacy file should be removed.
-            assertThat(owners.getLegacyConfigFile().exists()).isFalse();
-
-            assertThat(owners.getDeviceOwnerFile().exists()).isFalse();
-
-            assertThat(owners.getProfileOwnerFile(10).exists()).isTrue();
-            assertThat(owners.getProfileOwnerFile(11).exists()).isTrue();
-            assertThat(owners.getProfileOwnerFile(20).exists()).isFalse();
-            assertThat(owners.getProfileOwnerFile(21).exists()).isFalse();
-
-            assertThat(owners.hasDeviceOwner()).isFalse();
-            assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL);
-            assertThat(owners.getSystemUpdatePolicy()).isNull();
-            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
-                    DEVICE_OWNER_TYPE_DEFAULT);
-            assertThat(owners.getDeviceOwnerProtectedPackages(owners.getDeviceOwnerPackageName()))
-                    .isEmpty();
-
-            assertThat(owners.getProfileOwnerKeys()).hasSize(2);
-            assertThat(owners.getProfileOwnerComponent(10))
-                    .isEqualTo(new ComponentName("com.google.android.testdpc",
-                            "com.google.android.testdpc.DeviceAdminReceiver0"));
-            assertThat(owners.getProfileOwnerName(10)).isEqualTo("0");
-            assertThat(owners.getProfileOwnerPackage(10)).isEqualTo("com.google.android.testdpc");
-
-            assertThat(owners.getProfileOwnerComponent(11))
-                    .isEqualTo(new ComponentName("com.google.android.testdpc1", ""));
-            assertThat(owners.getProfileOwnerName(11)).isEqualTo("1");
-            assertThat(owners.getProfileOwnerPackage(11)).isEqualTo("com.google.android.testdpc1");
-
-            assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isTrue();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isTrue();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse();
-        }
-
-        // Then re-read and check.
-        {
-            final OwnersTestable owners = new OwnersTestable(getServices());
-            owners.load();
-
-            assertThat(owners.hasDeviceOwner()).isFalse();
-            assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL);
-            assertThat(owners.getSystemUpdatePolicy()).isNull();
-            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
-                    DEVICE_OWNER_TYPE_DEFAULT);
-            assertThat(owners.getDeviceOwnerProtectedPackages(owners.getDeviceOwnerPackageName()))
-                    .isEmpty();
-
-            assertThat(owners.getProfileOwnerKeys()).hasSize(2);
-            assertThat(owners.getProfileOwnerComponent(10))
-                    .isEqualTo(new ComponentName("com.google.android.testdpc",
-                            "com.google.android.testdpc.DeviceAdminReceiver0"));
-            assertThat(owners.getProfileOwnerName(10)).isEqualTo("0");
-            assertThat(owners.getProfileOwnerPackage(10)).isEqualTo("com.google.android.testdpc");
-
-            assertThat(owners.getProfileOwnerComponent(11))
-                    .isEqualTo(new ComponentName("com.google.android.testdpc1", ""));
-            assertThat(owners.getProfileOwnerName(11)).isEqualTo("1");
-            assertThat(owners.getProfileOwnerPackage(11)).isEqualTo("com.google.android.testdpc1");
-
-            assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isTrue();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isTrue();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse();
-        }
-    }
-
-    /**
-     * Note this also tests {@link Owners#setDeviceOwnerUserRestrictionsMigrated()}
-     * and {@link  Owners#setProfileOwnerUserRestrictionsMigrated(int)}.
-     */
-    @Test
-    public void testUpgrade04() throws Exception {
-        getServices().addUsers(10, 11, 20, 21);
-
-        // First, migrate.
-        {
-            final OwnersTestable owners = new OwnersTestable(getServices());
-
-            DpmTestUtils.writeToFile(owners.getLegacyConfigFile(),
-                    DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test04/input.xml"));
-
-            owners.load();
-
-            // The legacy file should be removed.
-            assertThat(owners.getLegacyConfigFile().exists()).isFalse();
-
-            assertThat(owners.getDeviceOwnerFile().exists()).isTrue();
-
-            assertThat(owners.getProfileOwnerFile(10).exists()).isTrue();
-            assertThat(owners.getProfileOwnerFile(11).exists()).isTrue();
-            assertThat(owners.getProfileOwnerFile(20).exists()).isFalse();
-            assertThat(owners.getProfileOwnerFile(21).exists()).isFalse();
-
-            assertThat(owners.hasDeviceOwner()).isTrue();
-            assertThat(owners.getDeviceOwnerName()).isEqualTo(null);
-            assertThat(owners.getDeviceOwnerPackageName()).isEqualTo("com.google.android.testdpc");
-            assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM);
-            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
-                    DEVICE_OWNER_TYPE_DEFAULT);
-            assertThat(owners.getDeviceOwnerProtectedPackages(owners.getDeviceOwnerPackageName()))
-                    .isEmpty();
-
-            assertThat(owners.getSystemUpdatePolicy()).isNotNull();
-            assertThat(owners.getSystemUpdatePolicy().getPolicyType()).isEqualTo(5);
-
-            assertThat(owners.getProfileOwnerKeys()).hasSize(2);
-            assertThat(owners.getProfileOwnerComponent(10))
-                    .isEqualTo(new ComponentName("com.google.android.testdpc",
-                            "com.google.android.testdpc.DeviceAdminReceiver0"));
-            assertThat(owners.getProfileOwnerName(10)).isEqualTo("0");
-            assertThat(owners.getProfileOwnerPackage(10)).isEqualTo("com.google.android.testdpc");
-
-            assertThat(owners.getProfileOwnerComponent(11))
-                    .isEqualTo(new ComponentName("com.google.android.testdpc1", ""));
-            assertThat(owners.getProfileOwnerName(11)).isEqualTo("1");
-            assertThat(owners.getProfileOwnerPackage(11)).isEqualTo("com.google.android.testdpc1");
-
-            assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isTrue();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isTrue();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isTrue();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse();
-        }
-
-        // Then re-read and check.
-        {
-            final OwnersTestable owners = new OwnersTestable(getServices());
-            owners.load();
-
-            assertThat(owners.hasDeviceOwner()).isTrue();
-            assertThat(owners.getDeviceOwnerName()).isEqualTo(null);
-            assertThat(owners.getDeviceOwnerPackageName()).isEqualTo("com.google.android.testdpc");
-            assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM);
-            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
-                    DEVICE_OWNER_TYPE_DEFAULT);
-            assertThat(owners.getDeviceOwnerProtectedPackages(owners.getDeviceOwnerPackageName()))
-                    .isEmpty();
-
-            assertThat(owners.getSystemUpdatePolicy()).isNotNull();
-            assertThat(owners.getSystemUpdatePolicy().getPolicyType()).isEqualTo(5);
-
-            assertThat(owners.getProfileOwnerKeys()).hasSize(2);
-            assertThat(owners.getProfileOwnerComponent(10))
-                    .isEqualTo(new ComponentName("com.google.android.testdpc",
-                            "com.google.android.testdpc.DeviceAdminReceiver0"));
-            assertThat(owners.getProfileOwnerName(10)).isEqualTo("0");
-            assertThat(owners.getProfileOwnerPackage(10)).isEqualTo("com.google.android.testdpc");
-
-            assertThat(owners.getProfileOwnerComponent(11))
-                    .isEqualTo(new ComponentName("com.google.android.testdpc1", ""));
-            assertThat(owners.getProfileOwnerName(11)).isEqualTo("1");
-            assertThat(owners.getProfileOwnerPackage(11)).isEqualTo("com.google.android.testdpc1");
-
-            assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isTrue();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isTrue();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isTrue();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse();
-
-            owners.setDeviceOwnerUserRestrictionsMigrated();
-
-            owners.setDeviceOwnerType(owners.getDeviceOwnerPackageName(),
-                    DEVICE_OWNER_TYPE_FINANCED, /* isAdminTestOnly= */ false);
-            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
-                    DEVICE_OWNER_TYPE_FINANCED);
-
-            owners.setDeviceOwnerProtectedPackages(
-                    owners.getDeviceOwnerPackageName(), DEVICE_OWNER_PROTECTED_PACKAGES);
-            assertThat(owners.getDeviceOwnerProtectedPackages(owners.getDeviceOwnerPackageName()))
-                    .isEqualTo(DEVICE_OWNER_PROTECTED_PACKAGES);
-            verify(getServices().packageManagerInternal)
-                    .setDeviceOwnerProtectedPackages(
-                            owners.getDeviceOwnerPackageName(), DEVICE_OWNER_PROTECTED_PACKAGES);
-        }
-
-        {
-            final OwnersTestable owners = new OwnersTestable(getServices());
-            owners.load();
-
-            assertThat(owners.hasDeviceOwner()).isTrue();
-            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
-                    DEVICE_OWNER_TYPE_FINANCED);
-            assertThat(owners.getDeviceOwnerProtectedPackages(owners.getDeviceOwnerPackageName()))
-                    .isEqualTo(DEVICE_OWNER_PROTECTED_PACKAGES);
-
-            assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isTrue();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isTrue();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse();
-
-            owners.setProfileOwnerUserRestrictionsMigrated(11);
-
-            owners.setDeviceOwnerType(owners.getDeviceOwnerPackageName(),
-                    DEVICE_OWNER_TYPE_DEFAULT, /* isAdminTestOnly= */ false);
-            // The previous device owner type should persist.
-            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
-                    DEVICE_OWNER_TYPE_FINANCED);
-
-            owners.setDeviceOwnerProtectedPackages(
-                    owners.getDeviceOwnerPackageName(), new ArrayList<>());
-            assertThat(owners.getDeviceOwnerProtectedPackages(owners.getDeviceOwnerPackageName()))
-                    .isEmpty();
-            verify(getServices().packageManagerInternal)
-                    .setDeviceOwnerProtectedPackages(
-                            owners.getDeviceOwnerPackageName(), new ArrayList<>());
-        }
-
-        {
-            final OwnersTestable owners = new OwnersTestable(getServices());
-            owners.load();
-
-            assertThat(owners.hasDeviceOwner()).isTrue();
-            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
-                    DEVICE_OWNER_TYPE_FINANCED);
-            assertThat(owners.getDeviceOwnerProtectedPackages(owners.getDeviceOwnerPackageName()))
-                    .isEmpty();
-
-            assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isTrue();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse();
-
-            owners.setProfileOwnerUserRestrictionsMigrated(11);
-        }
-    }
-
-    @Test
-    public void testUpgrade05() throws Exception {
-        getServices().addUsers(10, 11, 20, 21);
-
-        // First, migrate.
-        {
-            final OwnersTestable owners = new OwnersTestable(getServices());
-
-            DpmTestUtils.writeToFile(owners.getLegacyConfigFile(),
-                    DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test05/input.xml"));
-
-            owners.load();
-
-            // The legacy file should be removed.
-            assertThat(owners.getLegacyConfigFile().exists()).isFalse();
-
-            // Note device initializer is no longer supported.  No need to write the DO file.
-            assertThat(owners.getDeviceOwnerFile().exists()).isFalse();
-
-            assertThat(owners.getProfileOwnerFile(10).exists()).isFalse();
-            assertThat(owners.getProfileOwnerFile(11).exists()).isFalse();
-            assertThat(owners.getProfileOwnerFile(20).exists()).isFalse();
-
-            assertThat(owners.hasDeviceOwner()).isFalse();
-            assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL);
-            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
-                    DEVICE_OWNER_TYPE_DEFAULT);
-            assertThat(owners.getDeviceOwnerProtectedPackages(owners.getDeviceOwnerPackageName()))
-                    .isEmpty();
-
-
-            assertThat(owners.getSystemUpdatePolicy()).isNull();
-            assertThat(owners.getProfileOwnerKeys()).isEmpty();
-
-            assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse();
-        }
-
-        // Then re-read and check.
-        {
-            final OwnersTestable owners = new OwnersTestable(getServices());
-            owners.load();
-
-            assertThat(owners.hasDeviceOwner()).isFalse();
-            assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL);
-            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
-                    DEVICE_OWNER_TYPE_DEFAULT);
-            assertThat(owners.getDeviceOwnerProtectedPackages(owners.getDeviceOwnerPackageName()))
-                    .isEmpty();
-
-
-            assertThat(owners.getSystemUpdatePolicy()).isNull();
-            assertThat(owners.getProfileOwnerKeys()).isEmpty();
-
-            assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse();
-        }
-    }
-
-    @Test
-    public void testUpgrade06() throws Exception {
-        getServices().addUsers(10, 11, 20, 21);
-
-        // First, migrate.
-        {
-            final OwnersTestable owners = new OwnersTestable(getServices());
-
-            DpmTestUtils.writeToFile(owners.getLegacyConfigFile(),
-                    DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test06/input.xml"));
-
-            owners.load();
-
-            // The legacy file should be removed.
-            assertThat(owners.getLegacyConfigFile().exists()).isFalse();
-
-            assertThat(owners.getDeviceOwnerFile().exists()).isTrue();
-
-            assertThat(owners.getProfileOwnerFile(10).exists()).isFalse();
-            assertThat(owners.getProfileOwnerFile(11).exists()).isFalse();
-            assertThat(owners.getProfileOwnerFile(20).exists()).isFalse();
-
-            assertThat(owners.hasDeviceOwner()).isFalse();
-            assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL);
-            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
-                    DEVICE_OWNER_TYPE_DEFAULT);
-            assertThat(owners.getDeviceOwnerProtectedPackages(owners.getDeviceOwnerPackageName()))
-                    .isEmpty();
-            assertThat(owners.getProfileOwnerKeys()).isEmpty();
-
-            assertThat(owners.getSystemUpdatePolicy()).isNotNull();
-            assertThat(owners.getSystemUpdatePolicy().getPolicyType()).isEqualTo(5);
-
-            assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse();
-        }
-
-        // Then re-read and check.
-        {
-            final OwnersTestable owners = new OwnersTestable(getServices());
-            owners.load();
-
-            assertThat(owners.hasDeviceOwner()).isFalse();
-            assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL);
-            assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
-                    DEVICE_OWNER_TYPE_DEFAULT);
-            assertThat(owners.getDeviceOwnerProtectedPackages(owners.getDeviceOwnerPackageName()))
-                    .isEmpty();
-            assertThat(owners.getProfileOwnerKeys()).isEmpty();
-
-            assertThat(owners.getSystemUpdatePolicy()).isNotNull();
-            assertThat(owners.getSystemUpdatePolicy().getPolicyType()).isEqualTo(5);
-
-            assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse();
-            assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse();
-        }
-    }
-
-    @Test
-    public void testRemoveExistingFiles() throws Exception {
-        getServices().addUsers(10, 11, 20, 21);
+    public void loadProfileOwner() throws Exception {
+        getServices().addUsers(10);
 
         final OwnersTestable owners = new OwnersTestable(getServices());
 
-        // First, migrate to create new-style config files.
-        DpmTestUtils.writeToFile(owners.getLegacyConfigFile(),
-                DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/test04/input.xml"));
+        DpmTestUtils.writeToFile(owners.getProfileOwnerFile(10),
+                DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/profile_owner_1.xml"));
 
         owners.load();
 
-        assertThat(owners.getLegacyConfigFile().exists()).isFalse();
+        assertThat(owners.hasDeviceOwner()).isFalse();
+        assertThat(owners.getSystemUpdatePolicy()).isNull();
 
-        assertThat(owners.getDeviceOwnerFile().exists()).isTrue();
-        assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
-                DEVICE_OWNER_TYPE_DEFAULT);
-        assertThat(owners.getDeviceOwnerProtectedPackages(owners.getDeviceOwnerPackageName()))
-                .isEmpty();
-        assertThat(owners.getProfileOwnerFile(10).exists()).isTrue();
-        assertThat(owners.getProfileOwnerFile(11).exists()).isTrue();
+        assertThat(owners.getProfileOwnerKeys()).hasSize(1);
+        assertThat(owners.getProfileOwnerComponent(10))
+                .isEqualTo(new ComponentName(TESTDPC_PACKAGE,
+                        "com.afwsamples.testdpc.DeviceAdminReceiver"));
+    }
 
-        String previousDeviceOwnerPackageName = owners.getDeviceOwnerPackageName();
-        owners.setDeviceOwnerType(previousDeviceOwnerPackageName, DEVICE_OWNER_TYPE_FINANCED,
-                /* isAdminTestOnly= */ false);
-        assertThat(owners.getDeviceOwnerType(previousDeviceOwnerPackageName)).isEqualTo(
-                DEVICE_OWNER_TYPE_FINANCED);
-        owners.setDeviceOwnerProtectedPackages(
-                previousDeviceOwnerPackageName, DEVICE_OWNER_PROTECTED_PACKAGES);
-        assertThat(owners.getDeviceOwnerProtectedPackages(previousDeviceOwnerPackageName))
-                .isEqualTo(DEVICE_OWNER_PROTECTED_PACKAGES);
-        verify(getServices().packageManagerInternal)
-                .setDeviceOwnerProtectedPackages(
-                        owners.getDeviceOwnerPackageName(), DEVICE_OWNER_PROTECTED_PACKAGES);
+    @Test
+    public void loadDeviceOwner() throws Exception {
+        final OwnersTestable owners = new OwnersTestable(getServices());
 
-        // Then clear all information and save.
-        owners.clearDeviceOwner();
-        owners.clearSystemUpdatePolicy();
-        owners.removeProfileOwner(10);
-        owners.removeProfileOwner(11);
+        DpmTestUtils.writeToFile(owners.getDeviceOwnerFile(),
+                DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/device_owner_1.xml"));
 
-        owners.writeDeviceOwner();
-        owners.writeProfileOwner(10);
-        owners.writeProfileOwner(11);
-        owners.writeProfileOwner(20);
-        owners.writeProfileOwner(21);
+        owners.load();
 
-        // Now all files should be removed.
-        assertThat(owners.getDeviceOwnerFile().exists()).isFalse();
-        assertThat(owners.getProfileOwnerFile(10).exists()).isFalse();
-        assertThat(owners.getProfileOwnerFile(11).exists()).isFalse();
+        assertThat(owners.hasDeviceOwner()).isTrue();
 
-        assertThat(owners.getDeviceOwnerType(previousDeviceOwnerPackageName)).isEqualTo(
-                DEVICE_OWNER_TYPE_DEFAULT);
-        assertThat(owners.getDeviceOwnerProtectedPackages(previousDeviceOwnerPackageName))
-                .isEmpty();
-        verify(getServices().packageManagerInternal)
-                .setDeviceOwnerProtectedPackages(
-                        previousDeviceOwnerPackageName, new ArrayList<>());
+        assertThat(owners.getProfileOwnerKeys()).hasSize(0);
+        assertThat(owners.getDeviceOwnerComponent())
+                .isEqualTo(new ComponentName(TESTDPC_PACKAGE,
+                        "com.afwsamples.testdpc.DeviceAdminReceiver"));
+
+        assertThat(owners.getSystemUpdatePolicy().getPolicyType()).isEqualTo(TYPE_INSTALL_WINDOWED);
+    }
+
+    @Test
+    public void testDeviceOwnerType() throws Exception {
+        final OwnersTestable owners = new OwnersTestable(getServices());
+
+        DpmTestUtils.writeToFile(owners.getDeviceOwnerFile(),
+                DpmTestUtils.readAsset(mRealTestContext, "OwnersTest/device_owner_1.xml"));
+
+        owners.load();
+
+        assertThat(owners.getDeviceOwnerType(TESTDPC_PACKAGE))
+                .isEqualTo(DEVICE_OWNER_TYPE_DEFAULT);
+
+        // Should be able to set DO type to "financed".
+        owners.setDeviceOwnerType(
+                TESTDPC_PACKAGE, DEVICE_OWNER_TYPE_FINANCED, /* isAdminTestOnly= */ false);
+        assertThat(owners.getDeviceOwnerType(TESTDPC_PACKAGE))
+                .isEqualTo(DEVICE_OWNER_TYPE_FINANCED);
+
+        // Once set, DO type cannot be changed.
+        owners.setDeviceOwnerType(
+                TESTDPC_PACKAGE, DEVICE_OWNER_TYPE_DEFAULT, /* isAdminTestOnly= */ false);
+        assertThat(owners.getDeviceOwnerType(TESTDPC_PACKAGE))
+                .isEqualTo(DEVICE_OWNER_TYPE_FINANCED);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java
index d8f4349..3be2aac 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java
@@ -30,7 +30,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManagerInternal;
 import android.content.pm.Signature;
 import android.content.pm.SigningDetails;
 import android.content.pm.UserInfo;
@@ -48,7 +47,6 @@
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.component.ParsedActivity;
 import com.android.server.pm.pkg.component.ParsedActivityImpl;
 import com.android.server.pm.pkg.component.ParsedInstrumentationImpl;
@@ -69,6 +67,7 @@
 import java.security.cert.CertificateException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -103,10 +102,9 @@
     AppsFilterImpl.StateProvider mStateProvider;
     @Mock
     Executor mMockExecutor;
-    @Mock
-    PackageManagerInternal mMockPmInternal;
 
     private ArrayMap<String, PackageSetting> mExisting = new ArrayMap<>();
+    private Collection<SharedUserSetting> mSharedUserSettings = new ArraySet<>();
 
     private static ParsingPackage pkg(String packageName) {
         return PackageImpl.forTesting(packageName)
@@ -205,7 +203,7 @@
         MockitoAnnotations.initMocks(this);
         doAnswer(invocation -> {
             ((AppsFilterImpl.StateProvider.CurrentStateCallback) invocation.getArgument(0))
-                    .currentState(mExisting, USER_INFO_LIST);
+                    .currentState(mExisting, mSharedUserSettings, USER_INFO_LIST);
             return new Object();
         }).when(mStateProvider)
                 .runWithState(any(AppsFilterImpl.StateProvider.CurrentStateCallback.class));
@@ -226,7 +224,7 @@
     public void testSystemReadyPropogates() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         appsFilter.onSystemReady();
@@ -238,7 +236,7 @@
     public void testQueriesAction_FilterMatches() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         simulateAddBasicAndroid(appsFilter);
@@ -261,7 +259,7 @@
     public void testQueriesProtectedAction_FilterDoesNotMatch() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         final Signature frameworkSignature = Mockito.mock(Signature.class);
@@ -310,7 +308,7 @@
     public void testQueriesProvider_FilterMatches() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         simulateAddBasicAndroid(appsFilter);
@@ -335,7 +333,7 @@
     public void testOnUserUpdated_FilterMatches() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
 
         appsFilter.onSystemReady();
@@ -357,7 +355,7 @@
         // adds new user
         doAnswer(invocation -> {
             ((AppsFilterImpl.StateProvider.CurrentStateCallback) invocation.getArgument(0))
-                    .currentState(mExisting, USER_INFO_LIST_WITH_ADDED);
+                    .currentState(mExisting, mSharedUserSettings, USER_INFO_LIST_WITH_ADDED);
             return new Object();
         }).when(mStateProvider)
                 .runWithState(any(AppsFilterImpl.StateProvider.CurrentStateCallback.class));
@@ -374,7 +372,7 @@
         // delete user
         doAnswer(invocation -> {
             ((AppsFilterImpl.StateProvider.CurrentStateCallback) invocation.getArgument(0))
-                    .currentState(mExisting, USER_INFO_LIST);
+                    .currentState(mExisting, mSharedUserSettings, USER_INFO_LIST);
             return new Object();
         }).when(mStateProvider)
                 .runWithState(any(AppsFilterImpl.StateProvider.CurrentStateCallback.class));
@@ -393,7 +391,7 @@
     public void testQueriesDifferentProvider_Filters() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         simulateAddBasicAndroid(appsFilter);
@@ -418,7 +416,7 @@
     public void testQueriesProviderWithSemiColon_FilterMatches() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -437,7 +435,7 @@
     public void testQueriesAction_NoMatchingAction_Filters() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -454,7 +452,7 @@
     public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -475,7 +473,7 @@
     public void testNoQueries_Filters() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -492,7 +490,7 @@
     public void testNoUsesLibrary_Filters() throws Exception {
         final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock,
                 new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
-                mMockExecutor, mMockPmInternal);
+                mMockExecutor);
 
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
@@ -518,7 +516,7 @@
     public void testUsesLibrary_DoesntFilter() throws Exception {
         final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock,
                 new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
-                mMockExecutor, mMockPmInternal);
+                mMockExecutor);
 
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
@@ -545,7 +543,7 @@
     public void testUsesOptionalLibrary_DoesntFilter() throws Exception {
         final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock,
                 new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
-                mMockExecutor, mMockPmInternal);
+                mMockExecutor);
 
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
@@ -572,7 +570,7 @@
     public void testUsesLibrary_ShareUid_DoesntFilter() throws Exception {
         final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock,
                 new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
-                mMockExecutor, mMockPmInternal);
+                mMockExecutor);
 
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
@@ -604,7 +602,7 @@
     public void testForceQueryable_SystemDoesntFilter() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -623,7 +621,7 @@
     public void testForceQueryable_NonSystemFilters() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -641,7 +639,7 @@
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock,
                         new String[]{"com.some.package"}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -660,7 +658,7 @@
     public void testSystemSignedTarget_DoesntFilter() throws CertificateException {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         appsFilter.onSystemReady();
 
         final Signature frameworkSignature = Mockito.mock(Signature.class);
@@ -690,7 +688,7 @@
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock,
                         new String[]{"com.some.package"}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -708,8 +706,7 @@
     public void testSystemQueryable_DoesntFilter() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{},
-                        true /* system force queryable */, null, mMockExecutor,
-                        mMockPmInternal);
+                        true /* system force queryable */, null, mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -727,7 +724,7 @@
     public void testQueriesPackage_DoesntFilter() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -746,7 +743,7 @@
                 .thenReturn(false);
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -763,7 +760,7 @@
     public void testSystemUid_DoesntFilter() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -779,7 +776,7 @@
     public void testSystemUidSecondaryUser_DoesntFilter() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -796,7 +793,7 @@
     public void testNonSystemUid_NoCallingSetting_Filters() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -811,7 +808,7 @@
     public void testNoTargetPackage_filters() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -869,7 +866,7 @@
                         return Collections.emptyMap();
                     }
                 },
-                mMockExecutor, mMockPmInternal);
+                mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -925,16 +922,11 @@
                 .setOverlayTargetOverlayableName("overlayableName");
         ParsingPackage actorOne = pkg("com.some.package.actor.one");
         ParsingPackage actorTwo = pkg("com.some.package.actor.two");
-        ArraySet<PackageStateInternal> actorSharedSettingPackages = new ArraySet<>();
         PackageSetting ps1 = getPackageSettingFromParsingPackage(actorOne, DUMMY_ACTOR_APPID,
                 null /*settingBuilder*/);
         PackageSetting ps2 = getPackageSettingFromParsingPackage(actorTwo, DUMMY_ACTOR_APPID,
                 null /*settingBuilder*/);
-        actorSharedSettingPackages.add(ps1);
-        actorSharedSettingPackages.add(ps2);
-        when(mMockPmInternal.getSharedUserPackages(any(Integer.class))).thenReturn(
-                actorSharedSettingPackages
-        );
+
         final AppsFilterImpl appsFilter = new AppsFilterImpl(
                 mStateProvider,
                 mFeatureConfigMock,
@@ -965,7 +957,7 @@
                         return Collections.emptyMap();
                     }
                 },
-                mMockExecutor, mMockPmInternal);
+                mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -989,7 +981,7 @@
     public void testInitiatingApp_DoesntFilter() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -1007,7 +999,7 @@
     public void testUninstalledInitiatingApp_Filters() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -1025,7 +1017,7 @@
     public void testOriginatingApp_Filters() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         simulateAddBasicAndroid(appsFilter);
@@ -1050,7 +1042,7 @@
     public void testInstallingApp_DoesntFilter() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         simulateAddBasicAndroid(appsFilter);
@@ -1075,7 +1067,7 @@
     public void testInstrumentation_DoesntFilter() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         simulateAddBasicAndroid(appsFilter);
@@ -1104,7 +1096,7 @@
     public void testWhoCanSee() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         simulateAddBasicAndroid(appsFilter);
@@ -1177,7 +1169,7 @@
     public void testOnChangeReport() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
         watcher.register();
         simulateAddBasicAndroid(appsFilter);
@@ -1250,7 +1242,7 @@
     public void testOnChangeReportedFilter() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
         final WatchableTester watcher = new WatchableTester(appsFilter, "onChange filter");
@@ -1276,7 +1268,7 @@
     public void testAppsFilterRead() throws Exception {
         final AppsFilterImpl appsFilter =
                 new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
-                        mMockExecutor, mMockPmInternal);
+                        mMockExecutor);
         simulateAddBasicAndroid(appsFilter);
         appsFilter.onSystemReady();
 
@@ -1379,6 +1371,7 @@
         if (sharedUserSetting != null) {
             sharedUserSetting.addPackage(setting);
             setting.setSharedUserAppId(sharedUserSetting.mAppId);
+            mSharedUserSettings.add(sharedUserSetting);
         }
         filter.addPackage(setting);
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index a79a52c..e4ee4d06 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -538,6 +538,11 @@
         }
 
         @Override
+        ComponentName injectChooserActivity() {
+            return mInjectedChooserActivity;
+        }
+
+        @Override
         void wtf(String message, Throwable th) {
             // During tests, WTF is fatal.
             fail(message + "  exception: " + th + "\n" + Log.getStackTraceString(th));
@@ -678,6 +683,7 @@
 
     protected int mInjectedCallingUid;
     protected String mInjectedClientPackage;
+    protected ComponentName mInjectedChooserActivity;
 
     protected Map<String, PackageInfo> mInjectedPackages;
 
@@ -723,6 +729,9 @@
     protected static final String LAUNCHER_4 = "com.android.launcher.4";
     protected static final int LAUNCHER_UID_4 = 10014;
 
+    protected static final String CHOOSER_ACTIVITY_PACKAGE = "com.android.intentresolver";
+    protected static final int CHOOSER_ACTIVITY_UID = 10015;
+
     protected static final int USER_0 = UserHandle.USER_SYSTEM;
     protected static final int USER_10 = 10;
     protected static final int USER_11 = 11;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index a350dfb..867890f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -6901,6 +6901,9 @@
     }
 
     public void testGetShareTargets_permission() {
+        addPackage(CHOOSER_ACTIVITY_PACKAGE, CHOOSER_ACTIVITY_UID, 10, "sig1");
+        mInjectedChooserActivity =
+                ComponentName.createRelative(CHOOSER_ACTIVITY_PACKAGE, ".ChooserActivity");
         IntentFilter filter = new IntentFilter();
 
         assertExpectException(SecurityException.class, "Missing permission", () ->
@@ -6909,6 +6912,11 @@
         // Has permission, now it should pass.
         mCallerPermissions.add(permission.MANAGE_APP_PREDICTIONS);
         mManager.getShareTargets(filter);
+
+        runWithCaller(CHOOSER_ACTIVITY_PACKAGE, USER_0, () -> {
+            // Access is allowed when called from the configured system ChooserActivity
+            mManager.getShareTargets(filter);
+        });
     }
 
     public void testHasShareTargets_permission() {
diff --git a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java
index a3223d6d..8f5f0e6 100644
--- a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.power;
 
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -56,6 +58,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.concurrent.Executor;
+
 /**
  * Tests for {@link com.android.server.power.Notifier}
  */
@@ -79,6 +83,7 @@
     private Context mContextSpy;
     private Resources mResourcesSpy;
     private TestLooper mTestLooper = new TestLooper();
+    private FakeExecutor mTestExecutor = new FakeExecutor();
     private Notifier mNotifier;
 
     @Before
@@ -107,6 +112,7 @@
         // WHEN wired charging starts
         mNotifier.onWiredChargingStarted(USER_ID);
         mTestLooper.dispatchAll();
+        mTestExecutor.simulateAsyncExecutionOfLastCommand();
 
         // THEN the device vibrates once
         verify(mVibrator, times(1)).vibrate(any(), any(VibrationAttributes.class));
@@ -122,6 +128,7 @@
         // WHEN wired charging starts
         mNotifier.onWiredChargingStarted(USER_ID);
         mTestLooper.dispatchAll();
+        mTestExecutor.simulateAsyncExecutionOfLastCommand();
 
         // THEN the device doesn't vibrate
         verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class));
@@ -137,6 +144,7 @@
         // WHEN wireless charging starts
         mNotifier.onWirelessChargingStarted(5, USER_ID);
         mTestLooper.dispatchAll();
+        mTestExecutor.simulateAsyncExecutionOfLastCommand();
 
         // THEN the device vibrates once
         verify(mVibrator, times(1)).vibrate(any(), any(VibrationAttributes.class));
@@ -152,6 +160,7 @@
         // WHEN wireless charging starts
         mNotifier.onWirelessChargingStarted(5, USER_ID);
         mTestLooper.dispatchAll();
+        mTestExecutor.simulateAsyncExecutionOfLastCommand();
 
         // THEN the device doesn't vibrate
         verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class));
@@ -170,6 +179,7 @@
         // WHEN wired charging starts
         mNotifier.onWiredChargingStarted(USER_ID);
         mTestLooper.dispatchAll();
+        mTestExecutor.simulateAsyncExecutionOfLastCommand();
 
         // THEN the device doesn't vibrate
         verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class));
@@ -186,6 +196,7 @@
         // WHEN wireless charging starts
         mNotifier.onWirelessChargingStarted(5, USER_ID);
         mTestLooper.dispatchAll();
+        mTestExecutor.simulateAsyncExecutionOfLastCommand();
 
         // THEN the charging animation is triggered
         verify(mStatusBarManagerInternal, times(1)).showChargingAnimation(5);
@@ -202,6 +213,7 @@
         // WHEN wireless charging starts
         mNotifier.onWirelessChargingStarted(5, USER_ID);
         mTestLooper.dispatchAll();
+        mTestExecutor.simulateAsyncExecutionOfLastCommand();
 
         // THEN the charging animation never gets called
         verify(mStatusBarManagerInternal, never()).showChargingAnimation(anyInt());
@@ -211,7 +223,8 @@
         @Override
         Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
                 SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
-                FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) {
+                FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector,
+                Executor backgroundExecutor) {
             return mNotifierMock;
         }
 
@@ -300,6 +313,32 @@
                 mInjector.createSuspendBlocker(mService, "testBlocker"),
                 null,
                 null,
-                null);
+                null,
+                mTestExecutor);
     }
+
+    private static class FakeExecutor implements Executor {
+        private Runnable mLastCommand;
+
+        @Override
+        public void execute(Runnable command) {
+            assertNull(mLastCommand);
+            assertNotNull(command);
+            mLastCommand = command;
+        }
+
+        public Runnable getAndResetLastCommand() {
+            Runnable toReturn = mLastCommand;
+            mLastCommand = null;
+            return toReturn;
+        }
+
+        public void simulateAsyncExecutionOfLastCommand() {
+            Runnable toRun = getAndResetLastCommand();
+            if (toRun != null) {
+                toRun.run();
+            }
+        }
+    }
+
 }
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index c9721db..fbcad62 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -109,6 +109,7 @@
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -220,7 +221,8 @@
             @Override
             Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
                     SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
-                    FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) {
+                    FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector,
+                    Executor executor) {
                 return mNotifierMock;
             }
 
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
index 2d0dca2..c9fc033 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
@@ -61,6 +61,16 @@
     }
 
     @Override
+    public boolean isTelephonyTimeZoneDetectionSupported() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isGeoTimeZoneDetectionSupported() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public void dump(IndentingPrintWriter pw, String[] args) {
         mDumpCalled = true;
     }
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index 0c28d8c..0a50e79 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -193,7 +193,7 @@
     public void removeListener_noMoreCallbacksToListener() {
         mVibrationSettings.addListener(mListenerMock);
 
-        setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
+        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, 0);
         verify(mListenerMock).onChange();
 
         mVibrationSettings.removeListener(mListenerMock);
@@ -291,8 +291,6 @@
     public void shouldIgnoreVibration_withRingerModeSilent_ignoresRingtoneAndNotification() {
         // Vibrating settings on are overruled by ringer mode.
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
-        setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
-        setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
         setRingerMode(AudioManager.RINGER_MODE_SILENT);
 
         for (int usage : ALL_USAGES) {
@@ -360,44 +358,25 @@
             assertVibrationNotIgnoredForUsage(usage);
         }
     }
+
     @Test
-    public void shouldIgnoreVibration_withRingSettingsOff_disableRingtoneVibrations() {
+    public void shouldIgnoreVibration_withRingSettingsOff_allowsAllVibrations() {
+        // VIBRATE_WHEN_RINGING is deprecated and should have no effect on the ring vibration
+        // setting. The ramping ringer is also independent now, instead of a 3-state setting.
         setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
         setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
 
         for (int usage : ALL_USAGES) {
-            if (usage == USAGE_RINGTONE) {
-                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
-            } else {
-                assertVibrationNotIgnoredForUsage(usage);
-            }
+            assertVibrationNotIgnoredForUsage(usage);
             assertVibrationNotIgnoredForUsageAndFlags(usage,
                     VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF);
         }
     }
 
     @Test
-    public void shouldIgnoreVibration_withRingSettingsOn_allowsAllVibrations() {
-        setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
-        setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
-
-        for (int usage : ALL_USAGES) {
-            assertVibrationNotIgnoredForUsage(usage);
-        }
-    }
-
-    @Test
-    public void shouldIgnoreVibration_withRampingRingerOn_allowsAllVibrations() {
-        setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
-        setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
-
-        for (int usage : ALL_USAGES) {
-            assertVibrationNotIgnoredForUsage(usage);
-        }
-    }
-
-    @Test
     public void shouldIgnoreVibration_withHapticFeedbackDisabled_ignoresTouchVibration() {
+        // HAPTIC_FEEDBACK_ENABLED is deprecated but it was the only setting used to disable touch
+        // feedback vibrations. Continue to apply this on top of the intensity setting.
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 0);
 
         for (int usage : ALL_USAGES) {
@@ -459,8 +438,6 @@
     @Test
     public void shouldIgnoreVibration_withRingSettingsOff_ignoresRingtoneVibrations() {
         // Vibrating settings on are overruled by ring intensity setting.
-        setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
-        setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
         setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
         setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
 
@@ -479,7 +456,6 @@
     public void shouldIgnoreVibration_updateTriggeredAfterInternalRingerModeChanged() {
         // Vibrating settings on are overruled by ringer mode.
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
-        setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
         setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
         setRingerMode(AudioManager.RINGER_MODE_NORMAL);
 
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 4fbf006..c735bb7 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -559,30 +559,26 @@
     }
 
     @Test
-    public void vibrate_withRingtone_usesRingtoneSettings() throws Exception {
+    public void vibrate_withRingtone_usesRingerModeSettings() throws Exception {
         mockVibrators(1);
         FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
         fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK,
                 VibrationEffect.EFFECT_HEAVY_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK);
 
-        setRingerMode(AudioManager.RINGER_MODE_NORMAL);
-        setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
-        setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
+        setRingerMode(AudioManager.RINGER_MODE_SILENT);
         VibratorManagerService service = createSystemReadyService();
         vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
         // Wait before checking it never played.
         assertFalse(waitUntil(s -> !fakeVibrator.getAllEffectSegments().isEmpty(),
                 service, /* timeout= */ 50));
 
-        setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
-        setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
+        setRingerMode(AudioManager.RINGER_MODE_NORMAL);
         service = createSystemReadyService();
         vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), RINGTONE_ATTRS);
         assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1,
                 service, TEST_TIMEOUT_MILLIS));
 
-        setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
-        setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
+        setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
         service = createSystemReadyService();
         vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK), RINGTONE_ATTRS);
         assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 2,
@@ -1225,7 +1221,6 @@
         mockVibrators(1);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
         setRingerMode(AudioManager.RINGER_MODE_NORMAL);
-        setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
         createSystemReadyService();
 
         IBinder firstToken = mock(IBinder.class);
@@ -1296,21 +1291,17 @@
         ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, audioAttrs,
                 mock(IExternalVibrationController.class));
 
-        setRingerMode(AudioManager.RINGER_MODE_NORMAL);
-        setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
-        setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
+        setRingerMode(AudioManager.RINGER_MODE_SILENT);
         createSystemReadyService();
         int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
         assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
 
-        setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
-        setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
+        setRingerMode(AudioManager.RINGER_MODE_NORMAL);
         createSystemReadyService();
         scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
         assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
 
-        setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
-        setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
+        setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
         createSystemReadyService();
         scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
         assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 1cf9697..b987c69 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -170,6 +170,7 @@
 import android.service.notification.NotificationStats;
 import android.service.notification.StatusBarNotification;
 import android.service.notification.ZenPolicy;
+import android.telecom.TelecomManager;
 import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -290,6 +291,8 @@
     @Mock
     ActivityManager mActivityManager;
     @Mock
+    TelecomManager mTelecomManager;
+    @Mock
     Resources mResources;
     @Mock
     RankingHandler mRankingHandler;
@@ -494,7 +497,8 @@
                 mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
                 mAppOpsManager, mAppOpsService, mUm, mHistoryManager, mStatsManager,
                 mock(TelephonyManager.class),
-                mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class));
+                mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class),
+                mTelecomManager);
         // Return first true for RoleObserver main-thread check
         when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false);
         mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper);
@@ -9127,6 +9131,54 @@
     }
 
     @Test
+    public void testCallNotificationsBypassBlock() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+
+        Notification.Builder nb = new Notification.Builder(
+                mContext, mTestNotificationChannel.getId())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .addAction(new Notification.Action.Builder(null, "test", null).build());
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        mBinderService.setNotificationsEnabledForPackage(
+                r.getSbn().getPackageName(), r.getUid(), false);
+
+        // normal blocked notifications - blocked
+        assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+
+        // just using the style - blocked
+        Person person = new Person.Builder()
+                .setName("caller")
+                .build();
+        nb.setStyle(Notification.CallStyle.forOngoingCall(
+                person, mock(PendingIntent.class)));
+        nb.setFullScreenIntent(mock(PendingIntent.class), true);
+        sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+
+        // style + managed call - bypasses block
+        when(mTelecomManager.isInManagedCall()).thenReturn(true);
+        assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+
+        // style + self managed call - bypasses block
+        when(mTelecomManager.isInSelfManagedCall(
+                r.getSbn().getPackageName(), r.getUser())).thenReturn(true);
+        assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+    }
+
+    @Test
     public void testGetAllUsersNotificationPermissions_migrationNotEnabled() {
         // make sure we don't bother if the migration is not enabled
         assertThat(mService.getAllUsersNotificationPermissions()).isNull();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
index 0f6d5a5..b751c7f 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
@@ -97,6 +97,7 @@
 import android.provider.Settings;
 import android.service.notification.NotificationListenerFilter;
 import android.service.notification.StatusBarNotification;
+import android.telecom.TelecomManager;
 import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -379,7 +380,7 @@
                 mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
                 mAppOpsManager, mock(IAppOpsService.class), mUm, mHistoryManager, mStatsManager,
                 mock(TelephonyManager.class), mAmi, mToastRateLimiter, mPermissionHelper,
-                mock(UsageStatsManagerInternal.class));
+                mock(UsageStatsManagerInternal.class), mock(TelecomManager.class));
         // Return first true for RoleObserver main-thread check
         when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false);
         mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index 1d25b54..0bfd202 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -52,6 +52,7 @@
 import android.os.Looper;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.telecom.TelecomManager;
 import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -167,7 +168,7 @@
                     mock(StatsManager.class), mock(TelephonyManager.class),
                     mock(ActivityManagerInternal.class),
                     mock(MultiRateLimiter.class), mock(PermissionHelper.class),
-                    mock(UsageStatsManagerInternal.class));
+                    mock(UsageStatsManagerInternal.class), mock (TelecomManager.class));
         } catch (SecurityException e) {
             if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
                 throw e;
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index fe59185a..82e5411 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -20,6 +20,8 @@
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.window.BackNavigationInfo.typeToString;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
@@ -29,9 +31,12 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
@@ -39,6 +44,7 @@
 import android.hardware.HardwareBuffer;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.window.BackEvent;
 import android.window.BackNavigationInfo;
@@ -77,22 +83,29 @@
 
     @Test
     public void backNavInfo_HomeWhenBackToLauncher() {
-        IOnBackInvokedCallback callback = withSystemCallback(createTopTaskWithActivity());
+        IOnBackInvokedCallback callback =
+                withCallback(createTopTaskWithActivity(), OnBackInvokedDispatcher.PRIORITY_SYSTEM);
 
-        BackNavigationInfo backNavigationInfo = startBackNavigation();
+        SurfaceControl.Transaction tx = mock(SurfaceControl.Transaction.class);
+        BackNavigationInfo backNavigationInfo = mBackNavigationController.startBackNavigation(mWm,
+                tx);
         assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull();
         assertThat(backNavigationInfo.getDepartingAnimationTarget()).isNotNull();
         assertThat(backNavigationInfo.getTaskWindowConfiguration()).isNotNull();
         assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback);
         assertThat(typeToString(backNavigationInfo.getType()))
                 .isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME));
+
+        verify(tx, atLeastOnce()).apply();
+        verify(tx, times(1)).reparent(any(),
+                eq(backNavigationInfo.getDepartingAnimationTarget().leash));
     }
 
     @Test
     public void backTypeCrossTaskWhenBackToPreviousTask() {
         Task taskA = createTask(mDefaultDisplay);
         createActivityRecord(taskA);
-        withSystemCallback(createTopTaskWithActivity());
+        withCallback(createTopTaskWithActivity(), OnBackInvokedDispatcher.PRIORITY_SYSTEM);
         BackNavigationInfo backNavigationInfo = startBackNavigation();
         assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull();
         assertThat(typeToString(backNavigationInfo.getType()))
@@ -144,7 +157,7 @@
     @Test
     public void preparesForBackToHome() {
         Task task = createTopTaskWithActivity();
-        withSystemCallback(task);
+        withCallback(task, OnBackInvokedDispatcher.PRIORITY_SYSTEM);
 
         BackNavigationInfo backNavigationInfo = startBackNavigation();
         assertThat(typeToString(backNavigationInfo.getType()))
@@ -154,7 +167,8 @@
     @Test
     public void backTypeCallback() {
         Task task = createTopTaskWithActivity();
-        IOnBackInvokedCallback appCallback = withAppCallback(task);
+        IOnBackInvokedCallback appCallback =
+                withCallback(task, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
 
         BackNavigationInfo backNavigationInfo = startBackNavigation();
         assertThat(typeToString(backNavigationInfo.getType()))
@@ -215,18 +229,61 @@
                 1, appLatch.getCount());
     }
 
-    private IOnBackInvokedCallback withSystemCallback(Task task) {
+    @Test
+    public void returnsImeCallback_imeVisible() {
+        // Set up a top activity with a default priority callback.
+        IOnBackInvokedCallback appCallback =
+                withCallback(createTopTaskWithActivity(), OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+        IOnBackInvokedCallback imeCallback = createOnBackInvokedCallback();
+
+        // Set up an IME window with also a default priority callback.
+        final DisplayArea.Tokens imeContainer = mDisplayContent.getImeContainer();
+        final WindowState imeWindow = createImeWindow();
+        imeWindow.setOnBackInvokedCallbackInfo(
+                new OnBackInvokedCallbackInfo(
+                        imeCallback, OnBackInvokedDispatcher.PRIORITY_DEFAULT));
+        spyOn(imeContainer);
+        // Simulate IME becoming visible.
+        doReturn(true).when(imeContainer).isVisible();
+        doReturn(imeWindow).when(imeContainer).getWindow(any());
+        BackNavigationInfo backNavigationInfo = startBackNavigation();
+
+        // Expect the IME callback to be selected.
+        assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(imeCallback);
+    }
+
+    @Test
+    public void returnsAppOverlayCallback_imeVisible() {
+        // Set up a top activity with an overlay priority callback.
+        IOnBackInvokedCallback appCallback =
+                withCallback(createTopTaskWithActivity(), OnBackInvokedDispatcher.PRIORITY_OVERLAY);
+        IOnBackInvokedCallback imeCallback = createOnBackInvokedCallback();
+
+        // Set up an IME window with a default priority callback.
+        final DisplayArea.Tokens imeContainer = mDisplayContent.getImeContainer();
+        final WindowState imeWindow = createImeWindow();
+        imeWindow.setOnBackInvokedCallbackInfo(
+                new OnBackInvokedCallbackInfo(
+                        imeCallback, OnBackInvokedDispatcher.PRIORITY_DEFAULT));
+        spyOn(imeContainer);
+        // Simulate IME becoming visible.
+        doReturn(true).when(imeContainer).isVisible();
+        doReturn(imeWindow).when(imeContainer).getWindow(any());
+        BackNavigationInfo backNavigationInfo = startBackNavigation();
+
+        // Expect the app callback to be selected.
+        assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(appCallback);
+    }
+
+    private IOnBackInvokedCallback withCallback(Task task, int priority) {
         IOnBackInvokedCallback callback = createOnBackInvokedCallback();
         task.getTopMostActivity().getTopChild().setOnBackInvokedCallbackInfo(
-                new OnBackInvokedCallbackInfo(callback, OnBackInvokedDispatcher.PRIORITY_SYSTEM));
+                new OnBackInvokedCallbackInfo(callback, priority));
         return callback;
     }
 
-    private IOnBackInvokedCallback withAppCallback(Task task) {
-        IOnBackInvokedCallback callback = createOnBackInvokedCallback();
-        task.getTopMostActivity().getTopChild().setOnBackInvokedCallbackInfo(
-                new OnBackInvokedCallbackInfo(callback, OnBackInvokedDispatcher.PRIORITY_DEFAULT));
-        return callback;
+    private WindowState createImeWindow() {
+        return createWindow(null, W_INPUT_METHOD, "mImeWindow", 12345 /* fake ime uide */);
     }
 
     @Nullable
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 762c08f..8ef9ada 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -300,6 +300,81 @@
         assertTrue(firstActivity.mRequestForceTransition);
     }
 
+    /**
+     * When there is only one activity in the Task, and the activity is requesting to enter PIP, the
+     * whole Task should enter PIP.
+     */
+    @Test
+    public void testSingleActivityTaskEnterPip() {
+        final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setTask(fullscreenTask)
+                .build();
+        final Task task = activity.getTask();
+
+        // Move activity to pinned root task.
+        mRootWindowContainer.moveActivityToPinnedRootTask(activity,
+                null /* launchIntoPipHostActivity */, "test");
+
+        // Ensure a task has moved over.
+        ensureTaskPlacement(task, activity);
+        assertTrue(task.inPinnedWindowingMode());
+    }
+
+    /**
+     * When there is only one activity in the Task, and the activity is requesting to enter PIP, the
+     * whole Task should enter PIP even if the activity is in a TaskFragment.
+     */
+    @Test
+    public void testSingleActivityInTaskFragmentEnterPip() {
+        final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+                .setParentTask(fullscreenTask)
+                .createActivityCount(1)
+                .build();
+        final ActivityRecord activity = taskFragment.getTopMostActivity();
+        final Task task = activity.getTask();
+
+        // Move activity to pinned root task.
+        mRootWindowContainer.moveActivityToPinnedRootTask(activity,
+                null /* launchIntoPipHostActivity */, "test");
+
+        // Ensure a task has moved over.
+        ensureTaskPlacement(task, activity);
+        assertTrue(task.inPinnedWindowingMode());
+    }
+
+    /**
+     * When there is one TaskFragment with two activities in the Task, the activity requests to
+     * enter PIP, that activity will be move to PIP root task.
+     */
+    @Test
+    public void testMultipleActivitiesInTaskFragmentEnterPip() {
+        final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+                .setParentTask(fullscreenTask)
+                .createActivityCount(2)
+                .build();
+        final ActivityRecord firstActivity = taskFragment.getTopMostActivity();
+        final ActivityRecord secondActivity = taskFragment.getBottomMostActivity();
+
+        // Move first activity to pinned root task.
+        mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity,
+                null /* launchIntoPipHostActivity */, "test");
+
+        final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea();
+        final Task pinnedRootTask = taskDisplayArea.getRootPinnedTask();
+
+        // Ensure a task has moved over.
+        ensureTaskPlacement(pinnedRootTask, firstActivity);
+        ensureTaskPlacement(fullscreenTask, secondActivity);
+        assertTrue(pinnedRootTask.inPinnedWindowingMode());
+        assertEquals(WINDOWING_MODE_FULLSCREEN, fullscreenTask.getWindowingMode());
+    }
+
     private static void ensureTaskPlacement(Task task, ActivityRecord... activities) {
         final ArrayList<ActivityRecord> taskActivities = new ArrayList<>();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 37719fe..d135de0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -16,6 +16,9 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -28,6 +31,8 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
@@ -435,6 +440,107 @@
     }
 
     @Test
+    public void testTaskFragmentInPip_startActivityInTaskFragment() {
+        setupTaskFragmentInPip();
+        final ActivityRecord activity = mTaskFragment.getTopMostActivity();
+        final IBinder errorToken = new Binder();
+        spyOn(mAtm.getActivityStartController());
+        spyOn(mAtm.mWindowOrganizerController);
+
+        // Not allow to start activity in a TaskFragment that is in a PIP Task.
+        mTransaction.startActivityInTaskFragment(
+                mFragmentToken, activity.token, new Intent(), null /* activityOptions */)
+                .setErrorCallbackToken(errorToken);
+        mAtm.mWindowOrganizerController.applyTransaction(mTransaction);
+
+        verify(mAtm.getActivityStartController(), never()).startActivityInTaskFragment(any(), any(),
+                any(), any(), anyInt(), anyInt());
+        verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
+                eq(errorToken), any(IllegalArgumentException.class));
+    }
+
+    @Test
+    public void testTaskFragmentInPip_reparentActivityToTaskFragment() {
+        setupTaskFragmentInPip();
+        final ActivityRecord activity = createActivityRecord(mDisplayContent);
+        final IBinder errorToken = new Binder();
+        spyOn(mAtm.mWindowOrganizerController);
+
+        // Not allow to reparent activity to a TaskFragment that is in a PIP Task.
+        mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token)
+                .setErrorCallbackToken(errorToken);
+        mAtm.mWindowOrganizerController.applyTransaction(mTransaction);
+
+        verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
+                eq(errorToken), any(IllegalArgumentException.class));
+        assertNull(activity.getOrganizedTaskFragment());
+    }
+
+    @Test
+    public void testTaskFragmentInPip_setAdjacentTaskFragment() {
+        setupTaskFragmentInPip();
+        final IBinder errorToken = new Binder();
+        spyOn(mAtm.mWindowOrganizerController);
+
+        // Not allow to set adjacent on a TaskFragment that is in a PIP Task.
+        mTransaction.setAdjacentTaskFragments(mFragmentToken, null /* fragmentToken2 */,
+                null /* options */)
+                .setErrorCallbackToken(errorToken);
+        mAtm.mWindowOrganizerController.applyTransaction(mTransaction);
+
+        verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
+                eq(errorToken), any(IllegalArgumentException.class));
+        verify(mTaskFragment, never()).setAdjacentTaskFragment(any(), anyBoolean());
+    }
+
+    @Test
+    public void testTaskFragmentInPip_createTaskFragment() {
+        mController.registerOrganizer(mIOrganizer);
+        final Task pipTask = createTask(mDisplayContent, WINDOWING_MODE_PINNED,
+                ACTIVITY_TYPE_STANDARD);
+        final ActivityRecord activity = createActivityRecord(pipTask);
+        final IBinder fragmentToken = new Binder();
+        final IBinder errorToken = new Binder();
+        spyOn(mAtm.mWindowOrganizerController);
+
+        // Not allow to create TaskFragment in a PIP Task.
+        createTaskFragmentFromOrganizer(mTransaction, activity, fragmentToken);
+        mTransaction.setErrorCallbackToken(errorToken);
+        mAtm.mWindowOrganizerController.applyTransaction(mTransaction);
+
+        verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
+                eq(errorToken), any(IllegalArgumentException.class));
+        assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken));
+    }
+
+    @Test
+    public void testTaskFragmentInPip_deleteTaskFragment() {
+        setupTaskFragmentInPip();
+        final IBinder errorToken = new Binder();
+        spyOn(mAtm.mWindowOrganizerController);
+
+        // Not allow to delete a TaskFragment that is in a PIP Task.
+        mTransaction.deleteTaskFragment(mFragmentWindowToken)
+                .setErrorCallbackToken(errorToken);
+        mAtm.mWindowOrganizerController.applyTransaction(mTransaction);
+
+        verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
+                eq(errorToken), any(IllegalArgumentException.class));
+        assertNotNull(mAtm.mWindowOrganizerController.getTaskFragment(mFragmentToken));
+    }
+
+    @Test
+    public void testTaskFragmentInPip_setConfig() {
+        setupTaskFragmentInPip();
+        spyOn(mAtm.mWindowOrganizerController);
+
+        // Set bounds is ignored on a TaskFragment that is in a PIP Task.
+        mTransaction.setBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
+
+        verify(mTaskFragment, never()).setBounds(any());
+    }
+
+    @Test
     public void testDeferPendingTaskFragmentEventsOfInvisibleTask() {
         // Task - TaskFragment - Activity.
         final Task task = createTask(mDisplayContent);
@@ -643,4 +749,20 @@
             fail();
         }
     }
+
+    /** Setups an embedded TaskFragment in a PIP Task. */
+    private void setupTaskFragmentInPip() {
+        mOrganizer.applyTransaction(mTransaction);
+        mController.registerOrganizer(mIOrganizer);
+        mTaskFragment = new TaskFragmentBuilder(mAtm)
+                .setCreateParentTask()
+                .setFragmentToken(mFragmentToken)
+                .setOrganizer(mOrganizer)
+                .createActivityCount(1)
+                .build();
+        mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken();
+        mAtm.mWindowOrganizerController.mLaunchTaskFragments
+                .put(mFragmentToken, mTaskFragment);
+        mTaskFragment.getTask().setWindowingMode(WINDOWING_MODE_PINNED);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 2101b6e..54fa4e4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -31,6 +32,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.clearInvocations;
 
+import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.platform.test.annotations.Presubmit;
@@ -207,6 +209,71 @@
     }
 
     @Test
+    public void testEmbeddedTaskFragmentEnterPip_singleActivity_resetOrganizerOverrideConfig() {
+        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+                .setOrganizer(mOrganizer)
+                .setFragmentToken(new Binder())
+                .setCreateParentTask()
+                .createActivityCount(1)
+                .build();
+        final Task task = taskFragment.getTask();
+        final ActivityRecord activity = taskFragment.getTopMostActivity();
+        final Rect taskFragmentBounds = new Rect(0, 0, 300, 1000);
+        task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        taskFragment.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        taskFragment.setBounds(taskFragmentBounds);
+
+        assertEquals(taskFragmentBounds, activity.getBounds());
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode());
+
+        // Move activity to pinned root task.
+        mRootWindowContainer.moveActivityToPinnedRootTask(activity,
+                null /* launchIntoPipHostActivity */, "test");
+
+        // Ensure taskFragment requested config is reset.
+        assertEquals(taskFragment, activity.getOrganizedTaskFragment());
+        assertEquals(task, activity.getTask());
+        assertTrue(task.inPinnedWindowingMode());
+        assertTrue(taskFragment.inPinnedWindowingMode());
+        final Rect taskBounds = task.getBounds();
+        assertEquals(taskBounds, taskFragment.getBounds());
+        assertEquals(taskBounds, activity.getBounds());
+        assertEquals(Configuration.EMPTY, taskFragment.getRequestedOverrideConfiguration());
+    }
+
+    @Test
+    public void testEmbeddedTaskFragmentEnterPip_multiActivities_notifyOrganizer() {
+        final Task task = createTask(mDisplayContent);
+        final TaskFragment taskFragment0 = new TaskFragmentBuilder(mAtm)
+                .setParentTask(task)
+                .setOrganizer(mOrganizer)
+                .setFragmentToken(new Binder())
+                .createActivityCount(1)
+                .build();
+        final TaskFragment taskFragment1 = new TaskFragmentBuilder(mAtm)
+                .setParentTask(task)
+                .setOrganizer(mOrganizer)
+                .setFragmentToken(new Binder())
+                .createActivityCount(1)
+                .build();
+        final ActivityRecord activity0 = taskFragment0.getTopMostActivity();
+        spyOn(mAtm.mTaskFragmentOrganizerController);
+
+        // Move activity to pinned.
+        mRootWindowContainer.moveActivityToPinnedRootTask(activity0,
+                null /* launchIntoPipHostActivity */, "test");
+
+        // Ensure taskFragment requested config is reset.
+        assertTrue(taskFragment0.mClearedTaskFragmentForPip);
+        assertFalse(taskFragment1.mClearedTaskFragmentForPip);
+        final TaskFragmentInfo info = taskFragment0.getTaskFragmentInfo();
+        assertTrue(info.isTaskFragmentClearedForPip());
+        assertTrue(info.isEmpty());
+        verify(mAtm.mTaskFragmentOrganizerController)
+                .dispatchPendingInfoChangedEvent(taskFragment0);
+    }
+
+    @Test
     public void testActivityHasOverlayOverUntrustedModeEmbedded() {
         final Task rootTask = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,
                 ACTIVITY_TYPE_STANDARD);
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index b2cc104..1b07e9a 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -237,7 +237,12 @@
         @Override
         public void onUEvent(UEventObserver.UEvent event) {
             if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString());
-            sEventLogger.log(new UsbDeviceLogger.StringEvent("USB UEVENT: " + event.toString()));
+            if (sEventLogger != null) {
+                sEventLogger.log(new UsbDeviceLogger.StringEvent("USB UEVENT: "
+                        + event.toString()));
+            } else {
+                if (DEBUG) Slog.d(TAG, "sEventLogger == null");
+            }
 
             String state = event.get("USB_STATE");
             String accessory = event.get("ACCESSORY");
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
index 600fc27..94273a3 100644
--- a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
@@ -725,5 +725,15 @@
                         e);
             }
         }
+
+        @Override
+        public String getInterfaceHash() {
+            return IUsbCallback.HASH;
+        }
+
+        @Override
+        public int getInterfaceVersion() {
+            return IUsbCallback.VERSION;
+        }
     }
 }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 8d4a017..5b6e686 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -39,10 +39,13 @@
 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__NORMAL_DETECTOR;
 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECTED;
-import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_EXCEPTION;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_SECURITY_EXCEPTION;
 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_TIMEOUT;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_UNEXPECTED_CALLBACK;
 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER;
 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED_FROM_RESTART;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECT_UNEXPECTED_CALLBACK;
 import static com.android.server.voiceinteraction.SoundTriggerSessionPermissionsDecorator.enforcePermissionForPreflight;
 
 import android.annotation.NonNull;
@@ -133,6 +136,13 @@
     private static final int METRICS_INIT_CALLBACK_STATE_SUCCESS =
             HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__RESULT__CALLBACK_INIT_STATE_SUCCESS;
 
+    private static final int METRICS_KEYPHRASE_TRIGGERED_DETECT_SECURITY_EXCEPTION =
+            HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_SECURITY_EXCEPTION;
+    private static final int METRICS_KEYPHRASE_TRIGGERED_DETECT_UNEXPECTED_CALLBACK =
+            HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_UNEXPECTED_CALLBACK;
+    private static final int METRICS_KEYPHRASE_TRIGGERED_REJECT_UNEXPECTED_CALLBACK =
+            HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECT_UNEXPECTED_CALLBACK;
+
     private final Executor mAudioCopyExecutor = Executors.newCachedThreadPool();
     // TODO: This may need to be a Handler(looper)
     private final ScheduledExecutorService mScheduledExecutorService =
@@ -575,7 +585,7 @@
                         Slog.i(TAG, "Ignoring #onDetected due to a process restart");
                         HotwordMetricsLogger.writeKeyphraseTriggerEvent(
                                 mDetectorType,
-                                HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_EXCEPTION);
+                                METRICS_KEYPHRASE_TRIGGERED_DETECT_UNEXPECTED_CALLBACK);
                         return;
                     }
                     mValidatingDspTrigger = false;
@@ -584,7 +594,7 @@
                     } catch (SecurityException e) {
                         HotwordMetricsLogger.writeKeyphraseTriggerEvent(
                                 mDetectorType,
-                                HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_EXCEPTION);
+                                METRICS_KEYPHRASE_TRIGGERED_DETECT_SECURITY_EXCEPTION);
                         throw e;
                     }
                     externalCallback.onKeyphraseDetected(recognitionEvent, result);
@@ -614,7 +624,7 @@
                         Slog.i(TAG, "Ignoring #onRejected due to a process restart");
                         HotwordMetricsLogger.writeKeyphraseTriggerEvent(
                                 mDetectorType,
-                                HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_EXCEPTION);
+                                METRICS_KEYPHRASE_TRIGGERED_REJECT_UNEXPECTED_CALLBACK);
                         return;
                     }
                     mValidatingDspTrigger = false;
@@ -687,6 +697,9 @@
             // rejection. This also allows the Interactor to startReco again
             try {
                 mCallback.onRejected(new HotwordRejectedResult.Builder().build());
+                HotwordMetricsLogger.writeKeyphraseTriggerEvent(
+                        mDetectorType,
+                        HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED_FROM_RESTART);
             } catch (RemoteException e) {
                 Slog.w(TAG, "Failed to call #rejected");
             }
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 07e18d5..37403a8 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -355,6 +355,11 @@
 
     void setTestDefaultCallRedirectionApp(String packageName);
 
+    /**
+     * @see TelecomServiceImpl#requestLogMark
+     */
+    void requestLogMark(in String message);
+
     void setTestPhoneAcctSuggestionComponent(String flattenedComponentName);
 
     void setTestDefaultCallScreeningApp(String packageName);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a6a7c84..9c886aa 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -832,6 +832,17 @@
             "dial_string_replace_string_array";
 
     /**
+     * Specifies a map from dialstrings to replacements for international roaming network service
+     * numbers which cannot be replaced on the carrier side.
+     * <p>
+     * Individual entries have the format:
+     * [dialstring to replace]:[replacement]
+     * @hide
+     */
+    public static final String KEY_INTERNATIONAL_ROAMING_DIAL_STRING_REPLACE_STRING_ARRAY =
+            "international_roaming_dial_string_replace_string_array";
+
+    /**
      * Flag specifying whether WFC over IMS supports the "wifi only" option.  If false, the wifi
      * calling settings will not include an option for "wifi only".  If true, the wifi calling
      * settings will include an option for "wifi only"
@@ -8713,6 +8724,7 @@
         sDefaults.putStringArray(KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_DIAL_STRING_REPLACE_STRING_ARRAY, null);
+        sDefaults.putStringArray(KEY_INTERNATIONAL_ROAMING_DIAL_STRING_REPLACE_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_FORCE_HOME_NETWORK_BOOL, false);
         sDefaults.putInt(KEY_GSM_DTMF_TONE_DELAY_INT, 0);
         sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0);
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index d91134e..d978f57 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -23,16 +23,12 @@
 import android.os.Parcelable;
 import android.telephony.Annotation.NetworkType;
 
-import com.android.telephony.Rlog;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.Objects;
 
 public final class PhysicalChannelConfig implements Parcelable {
-    static final String TAG = "PhysicalChannelConfig";
-
     // TODO(b/72993578) consolidate these enums in a central location.
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -571,21 +567,19 @@
 
         public @NonNull Builder setNetworkType(@NetworkType int networkType) {
             if (!TelephonyManager.isNetworkTypeValid(networkType)) {
-                Rlog.e(TAG, "Builder.setNetworkType: Network type " + networkType + " is invalid.");
-            } else {
-                mNetworkType = networkType;
+                throw new IllegalArgumentException("Network type " + networkType + " is invalid.");
             }
+            mNetworkType = networkType;
             return this;
         }
 
         public @NonNull Builder setFrequencyRange(int frequencyRange) {
             if (!ServiceState.isFrequencyRangeValid(frequencyRange)
                     && frequencyRange != ServiceState.FREQUENCY_RANGE_UNKNOWN) {
-                Rlog.e(TAG, "Builder.setFrequencyRange: Frequency range " + frequencyRange
+                throw new IllegalArgumentException("Frequency range " + frequencyRange
                         + " is invalid.");
-            } else {
-                mFrequencyRange = frequencyRange;
             }
+            mFrequencyRange = frequencyRange;
             return this;
         }
 
@@ -601,21 +595,19 @@
 
         public @NonNull Builder setCellBandwidthDownlinkKhz(int cellBandwidthDownlinkKhz) {
             if (cellBandwidthDownlinkKhz < CELL_BANDWIDTH_UNKNOWN) {
-                Rlog.e(TAG, "Builder.setCellBandwidthDownlinkKhz: Cell downlink bandwidth(kHz) "
+                throw new IllegalArgumentException("Cell downlink bandwidth(kHz) "
                         + cellBandwidthDownlinkKhz + " is invalid.");
-            } else {
-                mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz;
             }
+            mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz;
             return this;
         }
 
         public @NonNull Builder setCellBandwidthUplinkKhz(int cellBandwidthUplinkKhz) {
             if (cellBandwidthUplinkKhz < CELL_BANDWIDTH_UNKNOWN) {
-                Rlog.e(TAG, "Builder.setCellBandwidthUplinkKhz: Cell uplink bandwidth(kHz) "
+                throw new IllegalArgumentException("Cell uplink bandwidth(kHz) "
                         + cellBandwidthUplinkKhz + " is invalid.");
-            } else {
-                mCellBandwidthUplinkKhz = cellBandwidthUplinkKhz;
             }
+            mCellBandwidthUplinkKhz = cellBandwidthUplinkKhz;
             return this;
         }
 
@@ -632,20 +624,18 @@
 
         public @NonNull Builder setPhysicalCellId(int physicalCellId) {
             if (physicalCellId > PHYSICAL_CELL_ID_MAXIMUM_VALUE) {
-                Rlog.e(TAG, "Builder.setPhysicalCellId: Physical cell ID " + physicalCellId
+                throw new IllegalArgumentException("Physical cell ID " + physicalCellId
                         + " is over limit.");
-            } else {
-                mPhysicalCellId = physicalCellId;
             }
+            mPhysicalCellId = physicalCellId;
             return this;
         }
 
         public @NonNull Builder setBand(int band) {
             if (band <= BAND_UNKNOWN) {
-                Rlog.e(TAG, "Builder.setBand: Band " + band + " is invalid.");
-            } else {
-                mBand = band;
+                throw new IllegalArgumentException("Band " + band + " is invalid.");
             }
+            mBand = band;
             return this;
         }
     }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index 4cddd85..aaa2db7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -34,8 +34,6 @@
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.flicker.replacesLayer
-import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
-import org.junit.Rule
 import org.junit.Test
 
 /**
@@ -45,9 +43,6 @@
     protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
     protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
 
-    @get:Rule
-    val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec)
-
     /**
      * Specification of the test transition to execute
      */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index 04fdda4..2f546b5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -63,8 +63,12 @@
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Group4
 class TaskTransitionTest(val testSpec: FlickerTestParameter) {
-    val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
     private val mTestApp: NewTasksAppHelper = NewTasksAppHelper(instrumentation)
+    private val mWallpaper by lazy {
+        getWallpaperPackage(InstrumentationRegistry.getInstrumentation())
+            ?: error("Unable to obtain wallpaper")
+    }
 
     @FlickerBuilderProvider
     fun buildFlicker(): FlickerBuilder {
@@ -97,7 +101,7 @@
     @Test
     fun wallpaperWindowIsNeverVisible() {
         testSpec.assertWm {
-            this.isNonAppWindowInvisible(WALLPAPER)
+            this.isNonAppWindowInvisible(mWallpaper)
         }
     }
 
@@ -109,7 +113,7 @@
     @Test
     fun wallpaperLayerIsNeverVisible() {
         testSpec.assertLayers {
-            this.isInvisible(WALLPAPER)
+            this.isInvisible(mWallpaper)
             this.isInvisible(WALLPAPER_BBQ_WRAPPER)
         }
     }
@@ -229,15 +233,14 @@
     fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
 
     companion object {
-        private val WALLPAPER = getWallpaperPackage(InstrumentationRegistry.getInstrumentation())
         private val LAUNCH_NEW_TASK_ACTIVITY =
                 LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME.toFlickerComponent()
         private val SIMPLE_ACTIVITY = SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent()
 
-        private fun getWallpaperPackage(instrumentation: Instrumentation): FlickerComponentName {
+        private fun getWallpaperPackage(instrumentation: Instrumentation): FlickerComponentName? {
             val wallpaperManager = WallpaperManager.getInstance(instrumentation.targetContext)
 
-            return wallpaperManager.wallpaperInfo.component.toFlickerComponent()
+            return wallpaperManager.wallpaperInfo?.component?.toFlickerComponent()
         }
 
         @Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 8b851e5..f0d16f3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -25,13 +25,11 @@
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
 import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
 import org.junit.FixMethodOrder
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
@@ -80,9 +78,6 @@
 class ChangeAppRotationTest(
     testSpec: FlickerTestParameter
 ) : RotationTransition(testSpec) {
-    @get:Rule
-    val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec)
-
     override val testApp = SimpleAppHelper(instrumentation)
     override val transition: FlickerBuilder.() -> Unit
         get() = {
diff --git a/tools/aapt/SdkConstants.h b/tools/aapt/SdkConstants.h
index f57c4ee..a146466 100644
--- a/tools/aapt/SdkConstants.h
+++ b/tools/aapt/SdkConstants.h
@@ -48,6 +48,7 @@
     SDK_R = 30,
     SDK_S = 31,
     SDK_S_V2 = 32,
+    SDK_TIRAMISU = 33,
     SDK_CUR_DEVELOPMENT = 10000,
 };
 
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index f2aaaf5..0bd61c0 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -58,6 +58,7 @@
   SDK_R = 30,
   SDK_S = 31,
   SDK_S_V2 = 32,
+  SDK_TIRAMISU = 33,
   SDK_CUR_DEVELOPMENT = 10000,
 };
 
diff --git a/tools/apilint/deprecated_at_birth.py b/tools/apilint/deprecated_at_birth.py
old mode 100644
new mode 100755
index 297d9c3b..d53c127
--- a/tools/apilint/deprecated_at_birth.py
+++ b/tools/apilint/deprecated_at_birth.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # Copyright (C) 2021 The Android Open Source Project
 #
@@ -44,6 +44,7 @@
     can be used to identify members across API levels."""
     raw = raw.replace(" deprecated ", " ")
     raw = raw.replace(" synchronized ", " ")
+    raw = raw.replace(" abstract ", " ")
     raw = raw.replace(" final ", " ")
     raw = re.sub("<.+?>", "", raw)
     raw = re.sub("@[A-Za-z]+ ", "", raw)
@@ -208,17 +209,17 @@
 
 def _parse_stream_path(path):
     api = {}
-    print "Parsing", path
+    print("Parsing %s" % path)
     for f in os.listdir(path):
         f = os.path.join(path, f)
         if not os.path.isfile(f): continue
         if not f.endswith(".txt"): continue
         if f.endswith("removed.txt"): continue
-        print "\t", f
+        print("\t%s" % f)
         with open(f) as s:
             api = _parse_stream(s, api)
-    print "Parsed", len(api), "APIs"
-    print
+    print("Parsed %d APIs" % len(api))
+    print()
     return api
 
 
@@ -306,8 +307,8 @@
             if "@Deprecated " in i.raw:
                 error(clazz, i, None, "Found API deprecation at birth " + i.ident)
 
-    print "%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True),
-                                            format(reset=True)))
+    print("%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True),
+                                            format(reset=True))))
     for f in sorted(failures):
-        print failures[f]
-        print
+        print(failures[f])
+        print()