Merge "Add name to conversation channels"
diff --git a/api/current.txt b/api/current.txt
index 1c03c23..44f9805 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6470,7 +6470,11 @@
     method public void disableCarMode(int);
     method public void enableCarMode(int);
     method public int getCurrentModeType();
+    method @NonNull public java.time.LocalTime getCustomNightModeEnd();
+    method @NonNull public java.time.LocalTime getCustomNightModeStart();
     method public int getNightMode();
+    method public void setCustomNightModeEnd(@NonNull java.time.LocalTime);
+    method public void setCustomNightModeStart(@NonNull java.time.LocalTime);
     method public void setNightMode(int);
     field public static String ACTION_ENTER_CAR_MODE;
     field public static String ACTION_ENTER_DESK_MODE;
@@ -6480,6 +6484,7 @@
     field public static final int ENABLE_CAR_MODE_ALLOW_SLEEP = 2; // 0x2
     field public static final int ENABLE_CAR_MODE_GO_CAR_HOME = 1; // 0x1
     field public static final int MODE_NIGHT_AUTO = 0; // 0x0
+    field public static final int MODE_NIGHT_CUSTOM = 3; // 0x3
     field public static final int MODE_NIGHT_NO = 1; // 0x1
     field public static final int MODE_NIGHT_YES = 2; // 0x2
   }
@@ -12034,7 +12039,6 @@
     field public static final String FEATURE_STRONGBOX_KEYSTORE = "android.hardware.strongbox_keystore";
     field public static final String FEATURE_TELEPHONY = "android.hardware.telephony";
     field public static final String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma";
-    field public static final String FEATURE_TELEPHONY_DATA = "android.hardware.telephony.data";
     field public static final String FEATURE_TELEPHONY_EUICC = "android.hardware.telephony.euicc";
     field public static final String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
     field public static final String FEATURE_TELEPHONY_IMS = "android.hardware.telephony.ims";
@@ -30996,6 +31000,7 @@
     field public static final String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS";
     field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 3; // 0x3
     field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 4; // 0x4
+    field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_INVALID = 7; // 0x7
     field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_NOT_ALLOWED = 6; // 0x6
     field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_APP_DISALLOWED = 2; // 0x2
     field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL = 1; // 0x1
@@ -50565,6 +50570,7 @@
     method public static java.util.TimeZone getTimeZone(int, boolean, long, String);
     method public static String getTimeZoneDatabaseVersion();
     method @Nullable public static java.util.List<java.lang.String> getTimeZoneIdsForCountryCode(@NonNull String);
+    method public static boolean isTimeBetween(@NonNull java.time.LocalTime, @NonNull java.time.LocalTime, @NonNull java.time.LocalTime);
   }
 
   @Deprecated public class TimingLogger {
diff --git a/api/system-current.txt b/api/system-current.txt
index f95f715..3909ca2 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -8472,6 +8472,7 @@
     field public static final String NAMESPACE_SYSTEMUI = "systemui";
     field public static final String NAMESPACE_TELEPHONY = "telephony";
     field public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier";
+    field public static final String NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT = "window_manager_native_boot";
   }
 
   public static class DeviceConfig.BadConfigException extends java.lang.Exception {
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index da0aae0..fde6bb3 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -1,33 +1,48 @@
 // Baseline format: 1.0
+AcronymName: android.net.NetworkCapabilities#setSSID(String):
+    Acronyms should not be capitalized in method names: was `setSSID`, should this be `setSsid`?
+
+
 ActionValue: android.location.Location#EXTRA_NO_GPS_LOCATION:
     
 ActionValue: android.net.wifi.WifiManager#ACTION_LINK_CONFIGURATION_CHANGED:
-    Inconsistent action value; expected `android.net.wifi.action.LINK_CONFIGURATION_CHANGED`, was `android.net.wifi.LINK_CONFIGURATION_CHANGED`
+    
 
 
+ArrayReturn: android.bluetooth.BluetoothCodecStatus#BluetoothCodecStatus(android.bluetooth.BluetoothCodecConfig, android.bluetooth.BluetoothCodecConfig[], android.bluetooth.BluetoothCodecConfig[]) parameter #1:
+    Method parameter should be Collection<BluetoothCodecConfig> (or subclass) instead of raw array; was `android.bluetooth.BluetoothCodecConfig[]`
+ArrayReturn: android.bluetooth.BluetoothCodecStatus#BluetoothCodecStatus(android.bluetooth.BluetoothCodecConfig, android.bluetooth.BluetoothCodecConfig[], android.bluetooth.BluetoothCodecConfig[]) parameter #2:
+    Method parameter should be Collection<BluetoothCodecConfig> (or subclass) instead of raw array; was `android.bluetooth.BluetoothCodecConfig[]`
+ArrayReturn: android.bluetooth.BluetoothCodecStatus#getCodecsLocalCapabilities():
+    Method should return Collection<BluetoothCodecConfig> (or subclass) instead of raw array; was `android.bluetooth.BluetoothCodecConfig[]`
+ArrayReturn: android.bluetooth.BluetoothCodecStatus#getCodecsSelectableCapabilities():
+    Method should return Collection<BluetoothCodecConfig> (or subclass) instead of raw array; was `android.bluetooth.BluetoothCodecConfig[]`
+ArrayReturn: android.bluetooth.BluetoothUuid#containsAnyUuid(android.os.ParcelUuid[], android.os.ParcelUuid[]) parameter #0:
+    Method parameter should be Collection<ParcelUuid> (or subclass) instead of raw array; was `android.os.ParcelUuid[]`
+ArrayReturn: android.bluetooth.BluetoothUuid#containsAnyUuid(android.os.ParcelUuid[], android.os.ParcelUuid[]) parameter #1:
+    Method parameter should be Collection<ParcelUuid> (or subclass) instead of raw array; was `android.os.ParcelUuid[]`
+ArrayReturn: android.media.tv.tuner.Tuner.FilterCallback#onFilterEvent(android.media.tv.tuner.Tuner.Filter, android.media.tv.tuner.filter.FilterEvent[]) parameter #1:
+    Method parameter should be Collection<FilterEvent> (or subclass) instead of raw array; was `android.media.tv.tuner.filter.FilterEvent[]`
+ArrayReturn: android.net.NetworkScoreManager#requestScores(android.net.NetworkKey[]) parameter #0:
+    Method parameter should be Collection<NetworkKey> (or subclass) instead of raw array; was `android.net.NetworkKey[]`
 ArrayReturn: android.view.contentcapture.ViewNode#getAutofillOptions():
     
 
 
 ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#deletePersistentGroup(android.net.wifi.p2p.WifiP2pManager.Channel, int, android.net.wifi.p2p.WifiP2pManager.ActionListener):
-    Registration methods should have overload that accepts delivery Executor: `deletePersistentGroup`
+    
 ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#factoryReset(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener):
-    Registration methods should have overload that accepts delivery Executor: `factoryReset`
+    
 ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#listen(android.net.wifi.p2p.WifiP2pManager.Channel, boolean, android.net.wifi.p2p.WifiP2pManager.ActionListener):
-    Registration methods should have overload that accepts delivery Executor: `listen`
+    
 ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#requestPersistentGroupInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.PersistentGroupInfoListener):
-    Registration methods should have overload that accepts delivery Executor: `requestPersistentGroupInfo`
+    
 ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#setDeviceName(android.net.wifi.p2p.WifiP2pManager.Channel, String, android.net.wifi.p2p.WifiP2pManager.ActionListener):
-    Registration methods should have overload that accepts delivery Executor: `setDeviceName`
+    
 ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#setWfdInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pWfdInfo, android.net.wifi.p2p.WifiP2pManager.ActionListener):
-    Registration methods should have overload that accepts delivery Executor: `setWfdInfo`
+    
 ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#setWifiP2pChannels(android.net.wifi.p2p.WifiP2pManager.Channel, int, int, android.net.wifi.p2p.WifiP2pManager.ActionListener):
-    Registration methods should have overload that accepts delivery Executor: `setWifiP2pChannels`
-
-HeavyBitSet: android.net.wifi.wificond.NativeScanResult#getCapabilities():
-    Type must not be heavy BitSet (method android.net.wifi.wificond.NativeScanResult.getCapabilities())
-PairedRegistration: android.net.wifi.wificond.WifiCondManager#registerApCallback(String, java.util.concurrent.Executor, android.net.wifi.wificond.WifiCondManager.SoftApCallback):
-    Found registerApCallback but not unregisterApCallback in android.net.wifi.wificond.WifiCondManager
+    
 
 
 GenericException: android.app.prediction.AppPredictor#finalize():
@@ -40,13 +55,22 @@
     
 
 
+HeavyBitSet: android.net.wifi.wificond.NativeScanResult#getCapabilities():
+    
 
 
+IntentBuilderName: android.content.Context#registerReceiverForAllUsers(android.content.BroadcastReceiver, android.content.IntentFilter, String, android.os.Handler):
+    Methods creating an Intent should be named `create<Foo>Intent()`, was `registerReceiverForAllUsers`
+
 
 KotlinKeyword: android.app.Notification#when:
     
 
 
+KotlinOperator: android.telephony.CbGeoUtils.Geometry#contains(android.telephony.CbGeoUtils.LatLng):
+    Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+
+
 MissingNullability: android.hardware.soundtrigger.SoundTrigger.ModuleProperties#toString():
     
 MissingNullability: android.hardware.soundtrigger.SoundTrigger.ModuleProperties#writeToParcel(android.os.Parcel, int) parameter #0:
@@ -70,7 +94,7 @@
 MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #1:
     
 MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #2:
-
+    
 MissingNullability: android.net.wifi.rtt.RangingRequest.Builder#addResponder(android.net.wifi.rtt.ResponderConfig):
     
 MissingNullability: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context) parameter #0:
@@ -157,43 +181,60 @@
     
 
 
-
 MutableBareField: android.net.IpConfiguration#httpProxy:
-    Bare field httpProxy must be marked final, or moved behind accessors if mutable
+    
 MutableBareField: android.net.IpConfiguration#ipAssignment:
-    Bare field ipAssignment must be marked final, or moved behind accessors if mutable
+    
 MutableBareField: android.net.IpConfiguration#proxySettings:
-    Bare field proxySettings must be marked final, or moved behind accessors if mutable
+    
 MutableBareField: android.net.IpConfiguration#staticIpConfiguration:
-    Bare field staticIpConfiguration must be marked final, or moved behind accessors if mutable
+    
 MutableBareField: android.net.wifi.WifiConfiguration#allowAutojoin:
     
 MutableBareField: android.net.wifi.WifiConfiguration#apBand:
-    Bare field apBand must be marked final, or moved behind accessors if mutable
+    
 MutableBareField: android.net.wifi.WifiConfiguration#carrierId:
     
 MutableBareField: android.net.wifi.WifiConfiguration#fromWifiNetworkSpecifier:
-    Bare field fromWifiNetworkSpecifier must be marked final, or moved behind accessors if mutable
+    
 MutableBareField: android.net.wifi.WifiConfiguration#fromWifiNetworkSuggestion:
-    Bare field fromWifiNetworkSuggestion must be marked final, or moved behind accessors if mutable
+    
 MutableBareField: android.net.wifi.WifiConfiguration#macRandomizationSetting:
-    Bare field macRandomizationSetting must be marked final, or moved behind accessors if mutable
+    
 MutableBareField: android.net.wifi.WifiConfiguration#meteredOverride:
-    Bare field meteredOverride must be marked final, or moved behind accessors if mutable
+    
 MutableBareField: android.net.wifi.WifiConfiguration#requirePMF:
-    Bare field requirePMF must be marked final, or moved behind accessors if mutable
+    
 MutableBareField: android.net.wifi.WifiConfiguration#saePasswordId:
-    Bare field saePasswordId must be marked final, or moved behind accessors if mutable
+    
 MutableBareField: android.net.wifi.WifiConfiguration#shared:
-    Bare field shared must be marked final, or moved behind accessors if mutable
+    
 MutableBareField: android.net.wifi.WifiScanner.ScanSettings#type:
-    Bare field type must be marked final, or moved behind accessors if mutable
+    
 
 
 NoClone: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0:
     
 
 
+NotCloseable: android.bluetooth.BluetoothA2dpSink:
+    Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.bluetooth.BluetoothA2dpSink
+NotCloseable: android.bluetooth.BluetoothMap:
+    Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.bluetooth.BluetoothMap
+NotCloseable: android.bluetooth.BluetoothPan:
+    Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.bluetooth.BluetoothPan
+NotCloseable: android.bluetooth.BluetoothPbap:
+    Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.bluetooth.BluetoothPbap
+
+
+OnNameExpected: android.content.ContentProvider#checkUriPermission(android.net.Uri, int, int):
+    If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+
+
+PairedRegistration: android.net.wifi.wificond.WifiCondManager#registerApCallback(String, java.util.concurrent.Executor, android.net.wifi.wificond.WifiCondManager.SoftApCallback):
+    
+
+
 ProtectedMember: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context):
     
 ProtectedMember: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]):
@@ -201,6 +242,7 @@
 ProtectedMember: android.service.notification.NotificationAssistantService#attachBaseContext(android.content.Context):
     
 
+
 SamShouldBeLast: android.accounts.AccountManager#addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
     
 SamShouldBeLast: android.accounts.AccountManager#addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean):
@@ -246,9 +288,11 @@
 SamShouldBeLast: android.app.AlarmManager#setWindow(int, long, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler):
     
 SamShouldBeLast: android.app.WallpaperInfo#dump(android.util.Printer, String):
-
+    
+SamShouldBeLast: android.app.WallpaperManager#addOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener, android.os.Handler):
+    SAM-compatible parameters (such as parameter 1, "listener", in android.app.WallpaperManager.addOnColorsChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
 SamShouldBeLast: android.app.admin.DevicePolicyManager#installSystemUpdate(android.content.ComponentName, android.net.Uri, java.util.concurrent.Executor, android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback):
-
+    
 SamShouldBeLast: android.content.Context#bindIsolatedService(android.content.Intent, int, String, java.util.concurrent.Executor, android.content.ServiceConnection):
     
 SamShouldBeLast: android.content.Context#bindService(android.content.Intent, int, java.util.concurrent.Executor, android.content.ServiceConnection):
@@ -279,12 +323,20 @@
     
 SamShouldBeLast: android.location.LocationManager#registerGnssStatusCallback(java.util.concurrent.Executor, android.location.GnssStatus.Callback):
     
+SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, android.location.LocationListener, android.os.Looper):
+    SAM-compatible parameters (such as parameter 4, "listener", in android.location.LocationManager.requestLocationUpdates) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
 SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, java.util.concurrent.Executor, android.location.LocationListener):
     
 SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(android.location.LocationRequest, java.util.concurrent.Executor, android.location.LocationListener):
     
+SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(long, float, android.location.Criteria, android.location.LocationListener, android.os.Looper):
+    SAM-compatible parameters (such as parameter 4, "listener", in android.location.LocationManager.requestLocationUpdates) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
 SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(long, float, android.location.Criteria, java.util.concurrent.Executor, android.location.LocationListener):
     
+SamShouldBeLast: android.location.LocationManager#requestSingleUpdate(String, android.location.LocationListener, android.os.Looper):
+    SAM-compatible parameters (such as parameter 2, "listener", in android.location.LocationManager.requestSingleUpdate) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.location.LocationManager#requestSingleUpdate(android.location.Criteria, android.location.LocationListener, android.os.Looper):
+    SAM-compatible parameters (such as parameter 2, "listener", in android.location.LocationManager.requestSingleUpdate) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
 SamShouldBeLast: android.media.AudioFocusRequest.Builder#setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener, android.os.Handler):
     
 SamShouldBeLast: android.media.AudioManager#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int):
@@ -380,7 +432,7 @@
 SamShouldBeLast: android.telephony.TelephonyManager#setPreferredOpportunisticDataSubscription(int, boolean, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
     
 SamShouldBeLast: android.telephony.TelephonyManager#updateAvailableNetworks(java.util.List<android.telephony.AvailableNetworkInfo>, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
-
+    
 SamShouldBeLast: android.view.View#postDelayed(Runnable, long):
     
 SamShouldBeLast: android.view.View#postOnAnimationDelayed(Runnable, long):
@@ -445,3 +497,11 @@
     
 ServiceName: android.provider.DeviceConfig#NAMESPACE_PACKAGE_MANAGER_SERVICE:
     
+
+
+UserHandle: android.companion.CompanionDeviceManager#isDeviceAssociated(String, android.net.MacAddress, android.os.UserHandle):
+    When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
+
+
+UserHandleName: android.telephony.CellBroadcastIntents#sendOrderedBroadcastForBackgroundReceivers(android.content.Context, android.os.UserHandle, android.content.Intent, String, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle):
+    Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `sendOrderedBroadcastForBackgroundReceivers`
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index f6014e5..d665f33 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -46,12 +46,10 @@
 
     void clearData(String pkg, int uid, boolean fromApp);
     // TODO: Replace parameter (ITransientNotification callback) with (CharSequence text)
-    void enqueueTextToast(String pkg, ITransientNotification callback, int duration, int displayId);
-    @UnsupportedAppUsage
-    void enqueueToast(String pkg, ITransientNotification callback, int duration, int displayId);
-    @UnsupportedAppUsage
-    void cancelToast(String pkg, ITransientNotification callback);
-    void finishToken(String pkg, ITransientNotification callback);
+    void enqueueTextToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId);
+    void enqueueToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId);
+    void cancelToast(String pkg, IBinder token);
+    void finishToken(String pkg, IBinder token);
 
     void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
             in Notification notification, int userId);
diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl
index f5809ba..41e2ec9 100644
--- a/core/java/android/app/IUiModeManager.aidl
+++ b/core/java/android/app/IUiModeManager.aidl
@@ -70,7 +70,27 @@
     boolean isNightModeLocked();
 
     /**
-    * @hide
+    * [De]Activates night mode
     */
     boolean setNightModeActivated(boolean active);
+
+    /**
+    * Returns custom start clock time
+    */
+    long getCustomNightModeStart();
+
+    /**
+    * Sets custom start clock time
+    */
+    void setCustomNightModeStart(long time);
+
+    /**
+    * Returns custom end clock time
+    */
+    long getCustomNightModeEnd();
+
+    /**
+    * Sets custom end clock time
+    */
+    void setCustomNightModeEnd(long time);
 }
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 3633064..24873b8 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.IntRange;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
@@ -32,6 +33,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.time.LocalTime;
 
 /**
  * This class provides access to the system uimode services.  These services
@@ -163,6 +165,7 @@
     /** @hide */
     @IntDef(prefix = { "MODE_" }, value = {
             MODE_NIGHT_AUTO,
+            MODE_NIGHT_CUSTOM,
             MODE_NIGHT_NO,
             MODE_NIGHT_YES
     })
@@ -173,19 +176,25 @@
      * Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
      * automatically switch night mode on and off based on the time.
      */
-    public static final int MODE_NIGHT_AUTO = Configuration.UI_MODE_NIGHT_UNDEFINED >> 4;
+    public static final int MODE_NIGHT_AUTO = 0;
+
+    /**
+     * Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
+     * automatically switch night mode on and off based on the time.
+     */
+    public static final int MODE_NIGHT_CUSTOM = 3;
     
     /**
      * Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
      * never run in night mode.
      */
-    public static final int MODE_NIGHT_NO = Configuration.UI_MODE_NIGHT_NO >> 4;
+    public static final int MODE_NIGHT_NO = 1;
     
     /**
      * Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
      * always run in night mode.
      */
-    public static final int MODE_NIGHT_YES = Configuration.UI_MODE_NIGHT_YES >> 4;
+    public static final int MODE_NIGHT_YES = 2;
 
     private IUiModeManager mService;
 
@@ -377,6 +386,8 @@
      *       {@code notnight} mode</li>
      *   <li><em>{@link #MODE_NIGHT_YES}</em> sets the device into
      *       {@code night} mode</li>
+     *   <li><em>{@link #MODE_NIGHT_CUSTOM}</em> automatically switches between
+     *       {@code night} and {@code notnight} based on the custom time set (or default)</li>
      *   <li><em>{@link #MODE_NIGHT_AUTO}</em> automatically switches between
      *       {@code night} and {@code notnight} based on the device's current
      *       location and certain other sensors</li>
@@ -418,6 +429,7 @@
      *   <li>{@link #MODE_NIGHT_NO}</li>
      *   <li>{@link #MODE_NIGHT_YES}</li>
      *   <li>{@link #MODE_NIGHT_AUTO}</li>
+     *   <li>{@link #MODE_NIGHT_CUSTOM}</li>
      *   <li>{@code -1} on error</li>
      * </ul>
      *
@@ -475,7 +487,7 @@
     }
 
     /**
-     * @hide*
+     * @hide
      */
     public boolean setNightModeActivated(boolean active) {
         if (mService != null) {
@@ -487,4 +499,75 @@
         }
         return false;
     }
+
+    /**
+     * Returns the time of the day Dark theme activates
+     * <p>
+     * When night mode is {@link #MODE_NIGHT_CUSTOM}, the system uses
+     * this time set to activate it automatically.
+     */
+    @NonNull
+    public LocalTime getCustomNightModeStart() {
+        if (mService != null) {
+            try {
+                return LocalTime.ofNanoOfDay(mService.getCustomNightModeStart() * 1000);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return LocalTime.MIDNIGHT;
+    }
+
+    /**
+     * Sets the time of the day Dark theme activates
+     * <p>
+     * When night mode is {@link #MODE_NIGHT_CUSTOM}, the system uses
+     * this time set to activate it automatically
+     * @param time The time of the day Dark theme should activate
+     */
+    public void setCustomNightModeStart(@NonNull LocalTime time) {
+        if (mService != null) {
+            try {
+                mService.setCustomNightModeStart(time.toNanoOfDay() / 1000);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Returns the time of the day Dark theme deactivates
+     * <p>
+     * When night mode is {@link #MODE_NIGHT_CUSTOM}, the system uses
+     * this time set to deactivate it automatically.
+     */
+    @NonNull
+    public LocalTime getCustomNightModeEnd() {
+        if (mService != null) {
+            try {
+                return LocalTime.ofNanoOfDay(mService.getCustomNightModeEnd() * 1000);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return LocalTime.MIDNIGHT;
+    }
+
+    /**
+     * Sets the time of the day Dark theme deactivates
+     * <p>
+     * When night mode is {@link #MODE_NIGHT_CUSTOM}, the system uses
+     * this time set to deactivate it automatically.
+     * @param time The time of the day Dark theme should deactivate
+     */
+    public void setCustomNightModeEnd(@NonNull LocalTime time) {
+        if (mService != null) {
+            try {
+                mService.setCustomNightModeEnd(time.toNanoOfDay() / 1000);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 1f502a1..925d70c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2323,13 +2323,6 @@
     public static final String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
 
     /**
-     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
-     * has a telephony radio that support data.
-     */
-    @SdkConstant(SdkConstantType.FEATURE)
-    public static final String FEATURE_TELEPHONY_DATA = "android.hardware.telephony.data";
-
-    /**
      * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
      * The device supports telephony carrier restriction mechanism.
      *
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 2377ccd..89dac2a 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -87,17 +87,31 @@
      */
      protected abstract <T> T getProtected(TKey key);
 
-     /**
-      * @hide
-      */
-     protected void setNativeInstance(CameraMetadataNative nativeInstance) {
+    /**
+     * @hide
+     */
+    protected void setNativeInstance(CameraMetadataNative nativeInstance) {
         mNativeInstance = nativeInstance;
-     }
+    }
 
-     /**
-      * @hide
-      */
-     protected abstract Class<TKey> getKeyClass();
+    /**
+     * Retrieves the native CameraMetadata* as a Java long.
+     * Returns 0 if mNativeInstance is null.
+     *
+     * @hide
+     */
+    public long getNativeMetadataPtr() {
+        if (mNativeInstance == null) {
+            return 0;
+        } else {
+            return mNativeInstance.getMetadataPtr();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    protected abstract Class<TKey> getKeyClass();
 
     /**
      * Returns a list of the keys contained in this map.
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 1fab666..3ae3d78 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -1703,6 +1703,15 @@
 
 
     /**
+     * Retrieves the pointer to the native CameraMetadata as a Java long.
+     *
+     * @hide
+     */
+    public long getMetadataPtr() {
+        return mMetadataPtr;
+    }
+
+    /**
      * Return a list containing keys of the given key class for all defined vendor tags.
      *
      * @hide
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 53f4615..84ceca0 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -333,6 +333,15 @@
     public static final String NAMESPACE_WINDOW_MANAGER = "window_manager";
 
     /**
+     * Namespace for window manager features accessible by native code and
+     * loaded once per boot.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT = "window_manager_native_boot";
+
+    /**
      * List of namespaces which can be read without READ_DEVICE_CONFIG permission
      *
      * @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index eee8fb1..0742a20 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5883,6 +5883,22 @@
                 "dark_mode_dialog_seen";
 
         /**
+         * Custom time when Dark theme is scheduled to activate.
+         * Represented as milliseconds from midnight (e.g. 79200000 == 10pm).
+         * @hide
+         */
+        public static final String DARK_THEME_CUSTOM_START_TIME =
+                "dark_theme_custom_start_time";
+
+        /**
+         * Custom time when Dark theme is scheduled to deactivate.
+         * Represented as milliseconds from midnight (e.g. 79200000 == 10pm).
+         * @hide
+         */
+        public static final String DARK_THEME_CUSTOM_END_TIME =
+                "dark_theme_custom_end_time";
+
+        /**
          * Defines value returned by {@link android.service.autofill.UserData#getMaxUserDataSize()}.
          *
          * @hide
@@ -7706,6 +7722,14 @@
         public static final String UI_NIGHT_MODE = "ui_night_mode";
 
         /**
+         * The current night mode that has been overrided by the system.  Owned
+         * and controlled by UiModeManagerService.  Constants are as per
+         * UiModeManager.
+         * @hide
+         */
+        public static final String UI_NIGHT_MODE_OVERRIDE = "ui_night_mode_override";
+
+        /**
          * Whether screensavers are enabled.
          * @hide
          */
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 8439f5a..0558204 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -30,6 +30,7 @@
 
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
+import java.time.LocalTime;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Collections;
@@ -382,6 +383,28 @@
     }
 
     /**
+     * This method is used to find if a clock time is inclusively between two other clock times
+     * @param reference The time of the day we want check if it is between start and end
+     * @param start The start time reference
+     * @param end The end time
+     * @return true if the reference time is between the two clock times, and false otherwise.
+     */
+    public static boolean isTimeBetween(@NonNull LocalTime reference,
+                                        @NonNull LocalTime start,
+                                        @NonNull LocalTime end) {
+        //    ////////E----+-----S////////
+        if ((reference.isBefore(start) && reference.isAfter(end)
+                //    -----+----S//////////E------
+                || (reference.isBefore(end) && reference.isBefore(start) && start.isBefore(end))
+                //    ---------S//////////E---+---
+                || (reference.isAfter(end) && reference.isAfter(start)) && start.isBefore(end))) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    /**
      * Dump a currentTimeMillis style timestamp for dumpsys, with the delta time from now.
      *
      * @hide
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 38416ee..bcc9e41 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -128,6 +128,8 @@
             int l, int t, int r, int b);
     private static native void nativeSetCornerRadius(long transactionObj, long nativeObject,
             float cornerRadius);
+    private static native void nativeSetBackgroundBlurRadius(long transactionObj, long nativeObject,
+            int blurRadius);
     private static native void nativeSetLayerStack(long transactionObj, long nativeObject,
             int layerStack);
 
@@ -2505,6 +2507,20 @@
         }
 
         /**
+         * Sets the background blur radius of the {@link SurfaceControl}.
+         *
+         * @param sc SurfaceControl.
+         * @param radius Blur radius in pixels.
+         * @return itself.
+         * @hide
+         */
+        public Transaction setBackgroundBlurRadius(SurfaceControl sc, int radius) {
+            checkPreconditions(sc);
+            nativeSetBackgroundBlurRadius(mNativeObject, sc.mNativeObject, radius);
+            return this;
+        }
+
+        /**
          * @hide
          */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index ccfbd7e..d39c3c0 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -29,6 +29,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
+import android.provider.DeviceConfig;
 import android.util.AndroidRuntimeException;
 import android.util.ArraySet;
 import android.util.Log;
@@ -56,11 +57,16 @@
 public final class WindowManagerGlobal {
     private static final String TAG = "WindowManager";
 
+    private static final String WM_USE_BLAST_ADAPTER_FLAG = "wm_use_blast_adapter";
+
     /**
      * This flag controls whether ViewRootImpl will utilize the Blast Adapter
      * to send buffer updates to SurfaceFlinger
      */
-    public static final boolean USE_BLAST_ADAPTER = false;
+    public static final boolean USE_BLAST_ADAPTER =
+            SystemProperties.getBoolean(String.join(".", "persist.device_config",
+                  DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
+                  WM_USE_BLAST_ADAPTER_FLAG), false);
 
     /**
      * The user is navigating with keys (not the touch screen), so
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 4e705db..969bda9 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -29,6 +29,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.PixelFormat;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
@@ -104,7 +105,8 @@
      */
     public static final int LENGTH_LONG = 1;
 
-    final Context mContext;
+    private final Binder mToken;
+    private final Context mContext;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     final TN mTN;
     @UnsupportedAppUsage
@@ -130,7 +132,8 @@
      */
     public Toast(@NonNull Context context, @Nullable Looper looper) {
         mContext = context;
-        mTN = new TN(context.getPackageName(), looper);
+        mToken = new Binder();
+        mTN = new TN(context.getPackageName(), mToken, looper);
         mTN.mY = context.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.toast_y_offset);
         mTN.mGravity = context.getResources().getInteger(
@@ -153,9 +156,9 @@
 
         try {
             if (mIsCustomToast) {
-                service.enqueueToast(pkg, tn, mDuration, displayId);
+                service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
             } else {
-                service.enqueueTextToast(pkg, tn, mDuration, displayId);
+                service.enqueueTextToast(pkg, mToken, tn, mDuration, displayId);
             }
         } catch (RemoteException e) {
             // Empty
@@ -435,7 +438,8 @@
 
         WindowManager mWM;
 
-        String mPackageName;
+        final String mPackageName;
+        final Binder mToken;
 
         @GuardedBy("mCallbacks")
         private final List<Callback> mCallbacks = new ArrayList<>();
@@ -443,7 +447,7 @@
         static final long SHORT_DURATION_TIMEOUT = 4000;
         static final long LONG_DURATION_TIMEOUT = 7000;
 
-        TN(String packageName, @Nullable Looper looper) {
+        TN(String packageName, Binder token, @Nullable Looper looper) {
             // XXX This should be changed to use a Dialog, with a Theme.Toast
             // defined that sets up the layout params appropriately.
             final WindowManager.LayoutParams params = mParams;
@@ -459,6 +463,7 @@
                     | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
 
             mPackageName = packageName;
+            mToken = token;
 
             if (looper == null) {
                 // Use Looper.myLooper() if looper is not specified.
@@ -490,7 +495,7 @@
                             // handleShow()
                             mNextView = null;
                             try {
-                                getService().cancelToast(mPackageName, TN.this);
+                                getService().cancelToast(mPackageName, mToken);
                             } catch (RemoteException e) {
                             }
                             break;
@@ -620,7 +625,7 @@
                 // Now that we've removed the view it's safe for the server to release
                 // the resources.
                 try {
-                    getService().finishToken(mPackageName, this);
+                    getService().finishToken(mPackageName, mToken);
                 } catch (RemoteException e) {
                 }
 
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 50a60a9..e0f9571 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -560,6 +560,14 @@
     transaction->setCornerRadius(ctrl, cornerRadius);
 }
 
+static void nativeSetBackgroundBlurRadius(JNIEnv* env, jclass clazz, jlong transactionObj,
+         jlong nativeObject, jint blurRadius) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+    SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+    transaction->setBackgroundBlurRadius(ctrl, blurRadius);
+}
+
 static void nativeSetLayerStack(JNIEnv* env, jclass clazz, jlong transactionObj,
         jlong nativeObject, jint layerStack) {
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -1369,6 +1377,8 @@
             (void*)nativeSetWindowCrop },
     {"nativeSetCornerRadius", "(JJF)V",
             (void*)nativeSetCornerRadius },
+    {"nativeSetBackgroundBlurRadius", "(JJI)V",
+            (void*)nativeSetBackgroundBlurRadius },
     {"nativeSetLayerStack", "(JJI)V",
             (void*)nativeSetLayerStack },
     {"nativeSetShadowRadius", "(JJF)V",
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index a85c8f4..ce03727 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2562,4 +2562,10 @@
     // CATEGORY: SETTINGS
     // OS: R
     OPEN_SUPPORTED_LINKS = 1824;
+    
+    // OPEN: Settings > Display > Dark theme > Set start time dialog
+    DIALOG_DARK_THEME_SET_START_TIME = 1825;
+
+    // OPEN: Settings > Display > Dark theme > Set end time dialog
+    DIALOG_DARK_THEME_SET_END_TIME = 1826;
 }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0c01e87..7fd444a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -281,6 +281,10 @@
          before automatically restore the default connection.  Set -1 if the connection
          does not require auto-restore. -->
     <!-- the 6th element indicates boot-time dependency-met value. -->
+    <!-- NOTE: The telephony module is no longer reading the configuration below for available
+         APN types.  The set of APN types and relevant settings are specified within the telephony
+         module and are non-configurable.  Whether or not data connectivity over a cellular network
+         is available at all is controlled by the flag: config_moble_data_capable. -->
     <string-array translatable="false" name="networkAttributes">
         <item>"wifi,1,1,1,-1,true"</item>
         <item>"mobile,0,0,0,-1,true"</item>
@@ -1867,6 +1871,12 @@
          Note: This config is deprecated, please use config_defaultSms instead. -->
     <string name="default_sms_application" translatable="false">com.android.messaging</string>
 
+    <!-- Flag indicating whether the current device allows data.
+         If true, this means that the device supports data connectivity through
+         the telephony network.
+         This can be overridden to false for devices that support voice and/or sms . -->
+    <bool name="config_mobile_data_capable">true</bool>
+
     <!-- Default web browser.  This is the package name of the application that will
          be the default browser when the device first boots.  Afterwards the user
          can select whatever browser app they wish to use as the default.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 28b0feac..527798d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -310,6 +310,7 @@
   <java-symbol type="bool" name="config_sip_wifi_only" />
   <java-symbol type="bool" name="config_sms_capable" />
   <java-symbol type="bool" name="config_sms_utf8_support" />
+  <java-symbol type="bool" name="config_mobile_data_capable" />
   <java-symbol type="bool" name="config_suspendWhenScreenOffDueToProximity" />
   <java-symbol type="bool" name="config_swipeDisambiguation" />
   <java-symbol type="bool" name="config_syncstorageengine_masterSyncAutomatically" />
diff --git a/packages/SystemUI/res/drawable/action_chip_background.xml b/packages/SystemUI/res/drawable/action_chip_background.xml
index fc3dfeb..ac227a6 100644
--- a/packages/SystemUI/res/drawable/action_chip_background.xml
+++ b/packages/SystemUI/res/drawable/action_chip_background.xml
@@ -19,7 +19,7 @@
     android:color="@color/global_screenshot_button_ripple">
     <item android:id="@android:id/background">
         <shape android:shape="rectangle">
-            <stroke android:width="1dp" android:color="@color/global_screenshot_button_text"/>
+            <stroke android:width="1dp" android:color="@color/global_screenshot_button_border"/>
             <solid android:color="@color/global_screenshot_button_background"/>
             <corners android:radius="@dimen/screenshot_button_corner_radius"/>
         </shape>
diff --git a/packages/SystemUI/res/drawable/ic_arrow_downward.xml b/packages/SystemUI/res/drawable/ic_arrow_downward.xml
new file mode 100644
index 0000000..ddd075d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_arrow_downward.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:pathData="M20,12l-1.41,-1.41L13,16.17V4h-2v12.17l-5.58,-5.59L4,12l8,8 8,-8z"
+        android:fillColor="@android:color/white"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml b/packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml
new file mode 100644
index 0000000..163015b7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <gradient
+        android:angle="90"
+        android:startColor="#1f000000"
+        android:endColor="#00000000"/>
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/screenshot_rounded_corners.xml b/packages/SystemUI/res/drawable/screenshot_rounded_corners.xml
new file mode 100644
index 0000000..fb8e9b7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/screenshot_rounded_corners.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners android:radius="@dimen/screenshot_button_corner_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index 995cb7d..1f7def2 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -1,51 +1,75 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
+<!--
+  ~ Copyright (C) 2011 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.
+  -->
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-    <ImageView android:id="@+id/global_screenshot_background"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent">
+    <ImageView
+        android:id="@+id/global_screenshot_background"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:src="@android:color/black"
-        android:visibility="gone" />
-    <ImageView android:id="@+id/global_screenshot"
+        android:visibility="gone"/>
+    <ImageView
+        android:id="@+id/global_screenshot_actions_background"
+        android:layout_height="400dp"
+        android:layout_width="match_parent"
+        android:layout_gravity="bottom|center"
+        android:src="@drawable/screenshot_actions_background_protection"
+        android:alpha="0"/>
+    <HorizontalScrollView
+        android:id="@+id/global_screenshot_actions_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom|center"
+        android:elevation="3dp"
+        android:fillViewport="true"
+        android:layout_marginHorizontal="@dimen/screenshot_action_container_margin_horizontal"
+        android:gravity="center"
+        android:paddingLeft="@dimen/screenshot_action_container_padding_left"
+        android:paddingRight="@dimen/screenshot_action_container_padding_right"
+        android:paddingVertical="@dimen/screenshot_action_container_padding_vertical"
+        android:visibility="gone"
+        android:scrollbars="none"
+        android:background="@drawable/action_chip_container_background">
+        <LinearLayout
+            android:id="@+id/global_screenshot_actions"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+    </HorizontalScrollView>
+    <ImageView
+        android:id="@+id/global_screenshot"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
-        android:background="@drawable/screenshot_panel"
+        android:elevation="8dp"
         android:visibility="gone"
-        android:adjustViewBounds="true" />
-    <ImageView android:id="@+id/global_screenshot_flash"
+        android:background="@drawable/screenshot_rounded_corners"
+        android:adjustViewBounds="true"/>
+    <ImageView
+        android:id="@+id/global_screenshot_flash"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:src="@android:color/white"
-        android:visibility="gone" />
+        android:visibility="gone"/>
     <com.android.systemui.screenshot.ScreenshotSelectorView
         android:id="@+id/global_screenshot_selector"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:visibility="gone"
         android:pointerIcon="crosshair"/>
-    <LinearLayout
-        android:id="@+id/global_screenshot_actions"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="bottom|center"
-        android:gravity="center"
-        android:paddingVertical="@dimen/screenshot_action_container_padding"
-        android:visibility="gone"
-        android:background="@drawable/action_chip_container_background"/>
+
 </FrameLayout>
diff --git a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
index 366abaa..79867a16b 100644
--- a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
@@ -36,5 +36,6 @@
         android:layout_height="wrap_content"
         android:layout_marginEnd="@dimen/screenshot_action_chip_padding_end"
         android:textSize="@dimen/screenshot_action_chip_text_size"
+        android:textStyle="bold"
         android:textColor="@color/global_screenshot_button_text"/>
 </com.android.systemui.screenshot.ScreenshotActionChip>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index c142465..8dd2a8b 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -182,8 +182,10 @@
 
     <!-- Global screenshot actions -->
     <color name="global_screenshot_button_background">#F5F5F5</color>
+    <color name="global_screenshot_button_text">#000000</color>
+    <color name="global_screenshot_button_border">@color/GM2_grey_300</color>
     <color name="global_screenshot_button_ripple">#1f000000</color>
-    <color name="global_screenshot_button_text">@color/GM2_blue_500</color>
+    <color name="global_screenshot_button_icon">@color/GM2_blue_500</color>
 
     <!-- GM2 colors -->
     <color name="GM2_grey_50">#F8F9FA</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 53df025..26af4ec 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -295,17 +295,24 @@
     <!-- The padding on the global screenshot background image -->
     <dimen name="global_screenshot_legacy_bg_padding">20dp</dimen>
     <dimen name="global_screenshot_bg_padding">20dp</dimen>
+    <dimen name="global_screenshot_x_scale">80dp</dimen>
+    <dimen name="screenshot_offset_y">48dp</dimen>
+    <dimen name="screenshot_offset_x">16dp</dimen>
+    <dimen name="screenshot_action_container_offset_y">32dp</dimen>
     <dimen name="screenshot_action_container_corner_radius">10dp</dimen>
-    <dimen name="screenshot_action_container_padding">10dp</dimen>
+    <dimen name="screenshot_action_container_padding_vertical">10dp</dimen>
+    <dimen name="screenshot_action_container_margin_horizontal">8dp</dimen>
+    <dimen name="screenshot_action_container_padding_left">100dp</dimen>
+    <dimen name="screenshot_action_container_padding_right">8dp</dimen>
     <!-- Radius of the chip background on global screenshot actions -->
     <dimen name="screenshot_button_corner_radius">20dp</dimen>
     <dimen name="screenshot_action_chip_margin_horizontal">4dp</dimen>
     <dimen name="screenshot_action_chip_padding_vertical">10dp</dimen>
     <dimen name="screenshot_action_chip_icon_size">20dp</dimen>
-    <dimen name="screenshot_action_chip_padding_start">4dp</dimen>
+    <dimen name="screenshot_action_chip_padding_start">8dp</dimen>
     <!-- Padding between icon and text -->
     <dimen name="screenshot_action_chip_padding_middle">8dp</dimen>
-    <dimen name="screenshot_action_chip_padding_end">12dp</dimen>
+    <dimen name="screenshot_action_chip_padding_end">16dp</dimen>
     <dimen name="screenshot_action_chip_text_size">14sp</dimen>
 
 
@@ -1079,6 +1086,10 @@
     <dimen name="logout_button_margin_bottom">12dp</dimen>
     <dimen name="logout_button_corner_radius">2dp</dimen>
 
+    <!--  Blur radius on status bar window and power menu  -->
+    <dimen name="min_window_blur_radius">1px</dimen>
+    <dimen name="max_window_blur_radius">100px</dimen>
+
     <!-- How much into a DisplayCutout's bounds we can go, on each side -->
     <dimen name="display_cutout_margin_consumption">0px</dimen>
     <!-- How much each bubble is elevated. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 9129938..639005b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -920,6 +920,10 @@
     <string name="quick_settings_dark_mode_secondary_label_on_at_sunset">On at sunset</string>
     <!-- QuickSettings: Secondary text for when the Dark Mode will be on until sunrise. [CHAR LIMIT=20] -->
     <string name="quick_settings_dark_mode_secondary_label_until_sunrise">Until sunrise</string>
+    <!-- QuickSettings: Secondary text for when the Dark theme will be enabled at some user-selected time. [CHAR LIMIT=20] -->
+    <string name="quick_settings_dark_mode_secondary_label_on_at">On at <xliff:g id="time" example="10 pm">%s</xliff:g></string>
+    <!-- QuickSettings: Secondary text for when the Dark theme or some other tile will be on until some user-selected time. [CHAR LIMIT=20] -->
+    <string name="quick_settings_dark_mode_secondary_label_until">Until <xliff:g id="time" example="7 am">%s</xliff:g></string>
 
     <!-- QuickSettings: NFC tile [CHAR LIMIT=NONE] -->
     <string name="quick_settings_nfc_label">NFC</string>
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index a6fa414..7506be09 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -19,17 +19,22 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.BootCompleteCache;
 import com.android.systemui.BootCompleteCacheImpl;
 import com.android.systemui.DumpController;
 import com.android.systemui.assist.AssistModule;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.statusbar.BlurUtils;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.StatusBarWindowBlurController;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.people.PeopleHubModule;
@@ -82,6 +87,16 @@
                 keyguardUpdateMonitor, dumpController);
     }
 
+    @Singleton
+    @Provides
+    @Nullable
+    static StatusBarWindowBlurController providesBlurController(BlurUtils blurUtils,
+            @Main Resources resources, SysuiStatusBarStateController statusBarStateController,
+            DumpController dumpController) {
+        return blurUtils.supportsBlursOnWindows() ? new StatusBarWindowBlurController(resources,
+                statusBarStateController, blurUtils, dumpController) : null;
+    }
+
     /** */
     @Binds
     public abstract NotificationRowBinder bindNotificationRowBinder(
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 36a4a10..3735198 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -98,6 +98,7 @@
 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.BlurUtils;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -157,6 +158,7 @@
     private final IActivityManager mIActivityManager;
     private final TelecomManager mTelecomManager;
     private final MetricsLogger mMetricsLogger;
+    private final BlurUtils mBlurUtils;
 
     private ArrayList<Action> mItems;
     private ActionsDialog mDialog;
@@ -177,6 +179,9 @@
     private final ScreenshotHelper mScreenshotHelper;
     private final ScreenRecordHelper mScreenRecordHelper;
     private final ActivityStarter mActivityStarter;
+    private final SysuiColorExtractor mSysuiColorExtractor;
+    private final IStatusBarService mStatusBarService;
+    private final NotificationShadeWindowController mNotificationShadeWindowController;
     private GlobalActionsPanelPlugin mPanelPlugin;
 
     /**
@@ -192,7 +197,10 @@
             ConfigurationController configurationController, ActivityStarter activityStarter,
             KeyguardStateController keyguardStateController, UserManager userManager,
             TrustManager trustManager, IActivityManager iActivityManager,
-            TelecomManager telecomManager, MetricsLogger metricsLogger) {
+            TelecomManager telecomManager, MetricsLogger metricsLogger,
+            BlurUtils blurUtils, SysuiColorExtractor colorExtractor,
+            IStatusBarService statusBarService,
+            NotificationShadeWindowController notificationShadeWindowController) {
         mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
         mWindowManagerFuncs = windowManagerFuncs;
         mAudioManager = audioManager;
@@ -208,6 +216,10 @@
         mIActivityManager = iActivityManager;
         mTelecomManager = telecomManager;
         mMetricsLogger = metricsLogger;
+        mBlurUtils = blurUtils;
+        mSysuiColorExtractor = colorExtractor;
+        mStatusBarService = statusBarService;
+        mNotificationShadeWindowController = notificationShadeWindowController;
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
@@ -443,8 +455,9 @@
                                 mKeyguardManager.isDeviceLocked())
                         : null;
 
-        ActionsDialog dialog = new ActionsDialog(
-                mContext, mAdapter, panelViewController, isControlsEnabled(mContext));
+        ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController,
+                mBlurUtils, mSysuiColorExtractor, mStatusBarService,
+                mNotificationShadeWindowController, isControlsEnabled(mContext));
         dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
         dialog.setKeyguardShowing(mKeyguardShowing);
 
@@ -1529,18 +1542,21 @@
         private ResetOrientationData mResetOrientationData;
         private boolean mHadTopUi;
         private final NotificationShadeWindowController mNotificationShadeWindowController;
-        private boolean mControlsEnabled;
+        private final BlurUtils mBlurUtils;
+        private final boolean mControlsEnabled;
 
         ActionsDialog(Context context, MyAdapter adapter,
-                GlobalActionsPanelPlugin.PanelViewController plugin,
+                GlobalActionsPanelPlugin.PanelViewController plugin, BlurUtils blurUtils,
+                SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
+                NotificationShadeWindowController notificationShadeWindowController,
                 boolean controlsEnabled) {
             super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
             mContext = context;
             mAdapter = adapter;
-            mColorExtractor = Dependency.get(SysuiColorExtractor.class);
-            mStatusBarService = Dependency.get(IStatusBarService.class);
-            mNotificationShadeWindowController =
-                    Dependency.get(NotificationShadeWindowController.class);
+            mBlurUtils = blurUtils;
+            mColorExtractor = sysuiColorExtractor;
+            mStatusBarService = statusBarService;
+            mNotificationShadeWindowController = notificationShadeWindowController;
             mControlsEnabled = controlsEnabled;
 
             // Window initialization
@@ -1735,9 +1751,11 @@
                     .setDuration(300)
                     .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                     .setUpdateListener(animation -> {
-                        int alpha = (int) ((Float) animation.getAnimatedValue()
-                                * mScrimAlpha * 255);
+                        float animatedValue = animation.getAnimatedFraction();
+                        int alpha = (int) (animatedValue * mScrimAlpha * 255);
                         mBackgroundDrawable.setAlpha(alpha);
+                        mBlurUtils.applyBlur(mGlobalActionsLayout.getViewRootImpl(),
+                                mBlurUtils.radiusForRatio(animatedValue));
                     })
                     .start();
         }
@@ -1759,9 +1777,11 @@
                     .withEndAction(this::completeDismiss)
                     .setInterpolator(new LogAccelerateInterpolator())
                     .setUpdateListener(animation -> {
-                        int alpha = (int) ((1f - (Float) animation.getAnimatedValue())
-                                * mScrimAlpha * 255);
+                        float animatedValue = 1f - animation.getAnimatedFraction();
+                        int alpha = (int) (animatedValue * mScrimAlpha * 255);
                         mBackgroundDrawable.setAlpha(alpha);
+                        mBlurUtils.applyBlur(mGlobalActionsLayout.getViewRootImpl(),
+                                mBlurUtils.radiusForRatio(animatedValue));
                     })
                     .start();
             dismissPanel();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index 7bc2a0d..8f1769b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -33,6 +33,8 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import javax.inject.Inject;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
 
 /**
  * Quick Settings tile for: Night Mode / Dark Theme / Dark Mode.
@@ -43,7 +45,7 @@
 public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements
         ConfigurationController.ConfigurationListener,
         BatteryController.BatteryStateChangeCallback {
-
+    public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a");
     private final Icon mIcon = ResourceIcon.get(
             com.android.internal.R.drawable.ic_qs_ui_mode_night);
     private final UiModeManager mUiModeManager;
@@ -88,17 +90,28 @@
     protected void handleUpdateState(BooleanState state, Object arg) {
         int uiMode = mUiModeManager.getNightMode();
         boolean powerSave = mBatteryController.isPowerSave();
-        boolean isAuto = uiMode == UiModeManager.MODE_NIGHT_AUTO;
         boolean nightMode = (mContext.getResources().getConfiguration().uiMode
                         & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
 
         if (powerSave) {
             state.secondaryLabel = mContext.getResources().getString(
                     R.string.quick_settings_dark_mode_secondary_label_battery_saver);
-        } else if (isAuto) {
+        } else if (uiMode == UiModeManager.MODE_NIGHT_AUTO) {
             state.secondaryLabel = mContext.getResources().getString(nightMode
                     ? R.string.quick_settings_dark_mode_secondary_label_until_sunrise
                     : R.string.quick_settings_dark_mode_secondary_label_on_at_sunset);
+        } else if (uiMode == UiModeManager.MODE_NIGHT_CUSTOM) {
+            final boolean use24HourFormat = android.text.format.DateFormat.is24HourFormat(mContext);
+            final LocalTime time;
+            if (nightMode) {
+                time = mUiModeManager.getCustomNightModeEnd();
+            } else {
+                time = mUiModeManager.getCustomNightModeStart();
+            }
+            state.secondaryLabel = mContext.getResources().getString(nightMode
+                    ? R.string.quick_settings_dark_mode_secondary_label_until
+                    : R.string.quick_settings_dark_mode_secondary_label_on_at,
+                    use24HourFormat ? time.toString() : formatter.format(time));
         } else {
             state.secondaryLabel = null;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 27c9555..50e9a51 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -29,19 +29,21 @@
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.Color;
+import android.graphics.Outline;
 import android.graphics.PixelFormat;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.drawable.Icon;
 import android.media.MediaActionSound;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -49,6 +51,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.util.DisplayMetrics;
@@ -60,8 +63,11 @@
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 import android.view.animation.Interpolator;
+import android.widget.HorizontalScrollView;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.Toast;
@@ -87,7 +93,7 @@
  * Class for handling device screen shots
  */
 @Singleton
-public class GlobalScreenshot {
+public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInsetsListener {
 
     /**
      * POD used in the AsyncTask which saves an image in the background.
@@ -98,6 +104,7 @@
         public Consumer<Uri> finisher;
         public GlobalScreenshot.ActionsReadyListener mActionsReadyListener;
         public int errorMsgResId;
+        public boolean createDeleteAction;
 
         void clearImage() {
             image = null;
@@ -106,7 +113,8 @@
     }
 
     abstract static class ActionsReadyListener {
-        abstract void onActionsReady(Uri imageUri, List<Notification.Action> actions);
+        abstract void onActionsReady(Uri imageUri, List<Notification.Action> smartActions,
+                List<Notification.Action> actions);
     }
 
     // These strings are used for communicating the action invoked to
@@ -130,38 +138,36 @@
     private static final int SCREENSHOT_DROP_OUT_DELAY = 500;
     private static final int SCREENSHOT_DROP_OUT_DURATION = 430;
     private static final int SCREENSHOT_DROP_OUT_SCALE_DURATION = 370;
-    private static final int SCREENSHOT_FAST_DROP_OUT_DURATION = 320;
     private static final float BACKGROUND_ALPHA = 0.5f;
-    private static final float SCREENSHOT_SCALE = 1f;
-    private static final float SCREENSHOT_DROP_IN_MIN_SCALE = SCREENSHOT_SCALE * 0.725f;
-    private static final float SCREENSHOT_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.45f;
-    private static final float SCREENSHOT_CORNER_MIN_SCALE = SCREENSHOT_SCALE * 0.2f;
-    private static final float SCREENSHOT_FAST_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.6f;
-    private static final float SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET = 0f;
-    private static final float SCREENSHOT_CORNER_MIN_SCALE_OFFSET = .1f;
+    private static final float SCREENSHOT_DROP_IN_MIN_SCALE = 0.725f;
+    private static final float ROUNDED_CORNER_RADIUS = .05f;
     private static final long SCREENSHOT_CORNER_TIMEOUT_MILLIS = 8000;
     private static final int MESSAGE_CORNER_TIMEOUT = 2;
 
     private final ScreenshotNotificationsController mNotificationsController;
 
-    private Context mContext;
-    private WindowManager mWindowManager;
-    private WindowManager.LayoutParams mWindowLayoutParams;
-    private Display mDisplay;
-    private DisplayMetrics mDisplayMetrics;
+    private final Context mContext;
+    private final WindowManager mWindowManager;
+    private final WindowManager.LayoutParams mWindowLayoutParams;
+    private final Display mDisplay;
+    private final DisplayMetrics mDisplayMetrics;
+
+    private final View mScreenshotLayout;
+    private final ScreenshotSelectorView mScreenshotSelectorView;
+    private final ImageView mBackgroundView;
+    private final ImageView mScreenshotView;
+    private final ImageView mScreenshotFlash;
+    private final HorizontalScrollView mActionsContainer;
+    private final LinearLayout mActionsView;
+    private final ImageView mBackgroundProtection;
 
     private Bitmap mScreenBitmap;
-    private View mScreenshotLayout;
-    private ScreenshotSelectorView mScreenshotSelectorView;
-    private ImageView mBackgroundView;
-    private ImageView mScreenshotView;
-    private ImageView mScreenshotFlash;
-    private LinearLayout mActionsView;
-
     private AnimatorSet mScreenshotAnimation;
 
-    private float mBgPadding;
-    private float mBgPaddingScale;
+    private float mScreenshotOffsetXPx;
+    private float mScreenshotOffsetYPx;
+    private float mScreenshotHeightPx;
+    private float mCornerScale;
 
     private AsyncTask<Void, Void, Void> mSaveInBgTask;
 
@@ -172,7 +178,7 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MESSAGE_CORNER_TIMEOUT:
-                    GlobalScreenshot.this.clearScreenshot();
+                    GlobalScreenshot.this.clearScreenshot("timeout");
                     break;
                 default:
                     break;
@@ -194,7 +200,20 @@
         mScreenshotLayout = layoutInflater.inflate(R.layout.global_screenshot, null);
         mBackgroundView = mScreenshotLayout.findViewById(R.id.global_screenshot_background);
         mScreenshotView = mScreenshotLayout.findViewById(R.id.global_screenshot);
+        mScreenshotView.setClipToOutline(true);
+        mScreenshotView.setOutlineProvider(new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+                outline.setRoundRect(new Rect(0, 0, view.getWidth(), view.getHeight()),
+                        ROUNDED_CORNER_RADIUS * view.getWidth());
+            }
+        });
+
+        mActionsContainer = mScreenshotLayout.findViewById(
+                R.id.global_screenshot_actions_container);
         mActionsView = mScreenshotLayout.findViewById(R.id.global_screenshot_actions);
+        mBackgroundProtection = mScreenshotLayout.findViewById(
+                R.id.global_screenshot_actions_background);
 
         mScreenshotFlash = mScreenshotLayout.findViewById(R.id.global_screenshot_flash);
         mScreenshotSelectorView = mScreenshotLayout.findViewById(R.id.global_screenshot_selector);
@@ -202,6 +221,9 @@
         mScreenshotSelectorView.setFocusable(true);
         mScreenshotSelectorView.setFocusableInTouchMode(true);
         mScreenshotLayout.setOnTouchListener((v, event) -> {
+            if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+                clearScreenshot("tap_outside");
+            }
             // Intercept and ignore all touch events
             return true;
         });
@@ -212,6 +234,8 @@
                 WindowManager.LayoutParams.TYPE_SCREENSHOT,
                 WindowManager.LayoutParams.FLAG_FULLSCREEN
                         | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                         | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
                 PixelFormat.TRANSLUCENT);
         mWindowLayoutParams.setTitle("ScreenshotAnimation");
@@ -222,16 +246,33 @@
         mDisplayMetrics = new DisplayMetrics();
         mDisplay.getRealMetrics(mDisplayMetrics);
 
-        // Scale has to account for both sides of the bg
-        mBgPadding = (float) resources.getDimensionPixelSize(R.dimen.global_screenshot_bg_padding);
-        mBgPaddingScale = mBgPadding / mDisplayMetrics.widthPixels;
-
+        mScreenshotOffsetXPx = resources.getDimensionPixelSize(R.dimen.screenshot_offset_x);
+        mScreenshotOffsetYPx = resources.getDimensionPixelSize(R.dimen.screenshot_offset_y);
+        mScreenshotHeightPx =
+                resources.getDimensionPixelSize(R.dimen.screenshot_action_container_offset_y);
+        mCornerScale = resources.getDimensionPixelSize(R.dimen.global_screenshot_x_scale)
+                / (float) mDisplayMetrics.widthPixels;
 
         // Setup the Camera shutter sound
         mCameraSound = new MediaActionSound();
         mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
     }
 
+    @Override // ViewTreeObserver.OnComputeInternalInsetsListener
+    public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
+        inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+        Region touchRegion = new Region();
+
+        Rect screenshotRect = new Rect();
+        mScreenshotView.getBoundsOnScreen(screenshotRect);
+        touchRegion.op(screenshotRect, Region.Op.UNION);
+        Rect actionsRect = new Rect();
+        mActionsContainer.getBoundsOnScreen(actionsRect);
+        touchRegion.op(actionsRect, Region.Op.UNION);
+
+        inoutInfo.touchableRegion.set(touchRegion);
+    }
+
     /**
      * Creates a new worker thread and saves the screenshot to the media store.
      */
@@ -241,6 +282,7 @@
         data.image = mScreenBitmap;
         data.finisher = finisher;
         data.mActionsReadyListener = actionsReadyListener;
+        data.createDeleteAction = false;
         if (mSaveInBgTask != null) {
             mSaveInBgTask.cancel(false);
         }
@@ -252,6 +294,8 @@
      * Takes a screenshot of the current display and shows an animation.
      */
     private void takeScreenshot(Consumer<Uri> finisher, Rect crop) {
+        clearScreenshot("new screenshot requested");
+
         int rot = mDisplay.getRotation();
         int width = crop.width();
         int height = crop.height();
@@ -269,6 +313,9 @@
         mScreenBitmap.setHasAlpha(false);
         mScreenBitmap.prepareToDraw();
 
+        mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
+        mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+
         // Start the post-screenshot animation
         startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels);
     }
@@ -314,12 +361,9 @@
                 return false;
             }
         });
-        mScreenshotLayout.post(new Runnable() {
-            @Override
-            public void run() {
-                mScreenshotSelectorView.setVisibility(View.VISIBLE);
-                mScreenshotSelectorView.requestFocus();
-            }
+        mScreenshotLayout.post(() -> {
+            mScreenshotSelectorView.setVisibility(View.VISIBLE);
+            mScreenshotSelectorView.requestFocus();
         });
     }
 
@@ -337,16 +381,20 @@
     /**
      * Clears current screenshot
      */
-    private void clearScreenshot() {
+    private void clearScreenshot(String reason) {
+        Log.e(TAG, "clearing screenshot: " + reason);
         if (mScreenshotLayout.isAttachedToWindow()) {
             mWindowManager.removeView(mScreenshotLayout);
         }
+        mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
+        mScreenshotLayout.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
 
         // Clear any references to the bitmap
         mScreenBitmap = null;
         mScreenshotView.setImageBitmap(null);
-        mActionsView.setVisibility(View.GONE);
+        mActionsContainer.setVisibility(View.GONE);
         mBackgroundView.setVisibility(View.GONE);
+        mBackgroundProtection.setAlpha(0f);
         mScreenshotView.setVisibility(View.GONE);
         mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
     }
@@ -374,7 +422,6 @@
             mScreenshotAnimation.removeAllListeners();
         }
 
-        mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
         ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation();
         ValueAnimator screenshotFadeOutAnim = createScreenshotToCornerAnimation(w, h);
         mScreenshotAnimation = new AnimatorSet();
@@ -385,16 +432,19 @@
                 // Save the screenshot once we have a bit of time now
                 saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
                     @Override
-                    void onActionsReady(Uri uri, List<Notification.Action> actions) {
+                    void onActionsReady(Uri uri, List<Notification.Action> smartActions,
+                            List<Notification.Action> actions) {
                         if (uri == null) {
                             mNotificationsController.notifyScreenshotError(
                                     R.string.screenshot_failed_to_capture_text);
                         } else {
                             mScreenshotHandler.post(() ->
-                                    createScreenshotActionsShadeAnimation(actions).start());
+                                    createScreenshotActionsShadeAnimation(smartActions,
+                                            actions).start());
                         }
                     }
                 });
+                mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
                 mScreenshotHandler.sendMessageDelayed(
                         mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT),
                         SCREENSHOT_CORNER_TIMEOUT_MILLIS);
@@ -435,14 +485,6 @@
             }
         };
 
-        Resources r = mContext.getResources();
-        if ((r.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
-                == Configuration.UI_MODE_NIGHT_YES) {
-            mScreenshotView.getBackground().setTint(Color.BLACK);
-        } else {
-            mScreenshotView.getBackground().setTintList(null);
-        }
-
         ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
         anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
         anim.addListener(new AnimatorListenerAdapter() {
@@ -453,8 +495,8 @@
                 mScreenshotView.setAlpha(0f);
                 mScreenshotView.setTranslationX(0f);
                 mScreenshotView.setTranslationY(0f);
-                mScreenshotView.setScaleX(SCREENSHOT_SCALE + mBgPaddingScale);
-                mScreenshotView.setScaleY(SCREENSHOT_SCALE + mBgPaddingScale);
+                mScreenshotView.setScaleX(1);
+                mScreenshotView.setScaleY(1);
                 mScreenshotView.setVisibility(View.VISIBLE);
                 mScreenshotFlash.setAlpha(0f);
                 mScreenshotFlash.setVisibility(View.VISIBLE);
@@ -469,9 +511,8 @@
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 float t = (Float) animation.getAnimatedValue();
-                float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale)
-                        - scaleInterpolator.getInterpolation(t)
-                        * (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE);
+                float scaleT = 1 - (scaleInterpolator.getInterpolation(t)
+                        * (1 - SCREENSHOT_DROP_IN_MIN_SCALE));
                 mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
                 mScreenshotView.setAlpha(t);
                 mScreenshotView.setScaleX(scaleT);
@@ -500,20 +541,19 @@
         };
 
         // Determine the bounds of how to scale
-        float halfScreenWidth = (w - 2f * mBgPadding) / 2f;
-        float halfScreenHeight = (h - 2f * mBgPadding) / 2f;
-        final float offsetPct = SCREENSHOT_CORNER_MIN_SCALE_OFFSET;
+        float halfScreenWidth = w / 2f;
+        float halfScreenHeight = h / 2f;
         final PointF finalPos = new PointF(
-                -halfScreenWidth + (SCREENSHOT_CORNER_MIN_SCALE + offsetPct) * halfScreenWidth,
-                halfScreenHeight - (SCREENSHOT_CORNER_MIN_SCALE + offsetPct) * halfScreenHeight);
+                -halfScreenWidth + mCornerScale * halfScreenWidth + mScreenshotOffsetXPx,
+                halfScreenHeight - mCornerScale * halfScreenHeight - mScreenshotOffsetYPx);
 
         // Animate the screenshot to the bottom left corner
         anim.setDuration(SCREENSHOT_DROP_OUT_DURATION);
         anim.addUpdateListener(animation -> {
             float t = (Float) animation.getAnimatedValue();
-            float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
+            float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE)
                     - scaleInterpolator.getInterpolation(t)
-                    * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_CORNER_MIN_SCALE);
+                    * (SCREENSHOT_DROP_IN_MIN_SCALE - mCornerScale);
             mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
             mScreenshotView.setScaleX(scaleT);
             mScreenshotView.setScaleY(scaleT);
@@ -523,24 +563,48 @@
         return anim;
     }
 
-    private ValueAnimator createScreenshotActionsShadeAnimation(List<Notification.Action> actions) {
+    private ValueAnimator createScreenshotActionsShadeAnimation(
+            List<Notification.Action> smartActions, List<Notification.Action> actions) {
         LayoutInflater inflater = LayoutInflater.from(mContext);
         mActionsView.removeAllViews();
+        mActionsContainer.setScrollX(0);
+        mScreenshotLayout.invalidate();
+        mScreenshotLayout.requestLayout();
+        mScreenshotLayout.getViewTreeObserver().dispatchOnGlobalLayout();
+
+        // By default the activities won't be able to start immediately; override this to keep
+        // the same behavior as if started from a notification
+        try {
+            ActivityManager.getService().resumeAppSwitches();
+        } catch (RemoteException e) {
+        }
+
+        for (Notification.Action smartAction : smartActions) {
+            ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate(
+                    R.layout.global_screenshot_action_chip, mActionsView, false);
+            actionChip.setText(smartAction.title);
+            actionChip.setIcon(smartAction.getIcon(), false);
+            actionChip.setPendingIntent(smartAction.actionIntent,
+                    () -> clearScreenshot("chip tapped"));
+            mActionsView.addView(actionChip);
+        }
 
         for (Notification.Action action : actions) {
             ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate(
                     R.layout.global_screenshot_action_chip, mActionsView, false);
             actionChip.setText(action.title);
             actionChip.setIcon(action.getIcon(), true);
-            actionChip.setOnClickListener(v -> {
-                try {
-                    action.actionIntent.send();
-                    clearScreenshot();
-                } catch (PendingIntent.CanceledException e) {
-                    Log.e(TAG,
-                            String.format("Intent cancelled (title: %s)", action.title), e);
-                }
-            });
+            actionChip.setPendingIntent(action.actionIntent, () -> clearScreenshot("chip tapped"));
+            if (action.actionIntent.getIntent().getAction().equals(Intent.ACTION_EDIT)) {
+                mScreenshotView.setOnClickListener(v -> {
+                    try {
+                        action.actionIntent.send();
+                        clearScreenshot("screenshot preview tapped");
+                    } catch (PendingIntent.CanceledException e) {
+                        Log.e(TAG, "Intent cancelled", e);
+                    }
+                });
+            }
             mActionsView.addView(actionChip);
         }
 
@@ -550,27 +614,29 @@
             Toast scrollNotImplemented = Toast.makeText(
                     mContext, "Not implemented", Toast.LENGTH_SHORT);
             scrollChip.setText("Extend"); // TODO (mkephart): add resource and translate
+            scrollChip.setIcon(
+                    Icon.createWithResource(mContext, R.drawable.ic_arrow_downward), true);
             scrollChip.setOnClickListener(v -> scrollNotImplemented.show());
             mActionsView.addView(scrollChip);
         }
 
         ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
-        mActionsView.setY(mDisplayMetrics.heightPixels);
-        mActionsView.setVisibility(VISIBLE);
-        mActionsView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
-        float actionsViewHeight = mActionsView.getMeasuredHeight();
-        float screenshotStartHeight = mScreenshotView.getTranslationY();
+        mActionsContainer.setY(mDisplayMetrics.heightPixels);
+        mActionsContainer.setVisibility(VISIBLE);
+        mActionsContainer.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+        float actionsViewHeight = mActionsContainer.getMeasuredHeight() + mScreenshotHeightPx;
 
         animator.addUpdateListener(animation -> {
             float t = animation.getAnimatedFraction();
-            mScreenshotView.setTranslationY(screenshotStartHeight - actionsViewHeight * t);
-            mActionsView.setY(mDisplayMetrics.heightPixels - actionsViewHeight * t);
+            mBackgroundProtection.setAlpha(t);
+            mActionsContainer.setY(mDisplayMetrics.heightPixels - actionsViewHeight * t);
         });
         animator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 super.onAnimationEnd(animation);
                 mScreenshotView.requestFocus();
+                mScreenshotView.setElevation(50);
             }
         });
         return animator;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
index 11aa80b..16447fb 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
@@ -184,6 +184,7 @@
         data.image = mScreenBitmap;
         data.finisher = finisher;
         data.mActionsReadyListener = actionsReadyListener;
+        data.createDeleteAction = true;
         if (mSaveInBgTask != null) {
             mSaveInBgTask.cancel(false);
         }
@@ -264,12 +265,9 @@
                 return false;
             }
         });
-        mScreenshotLayout.post(new Runnable() {
-            @Override
-            public void run() {
-                mScreenshotSelectorView.setVisibility(View.VISIBLE);
-                mScreenshotSelectorView.requestFocus();
-            }
+        mScreenshotLayout.post(() -> {
+            mScreenshotSelectorView.setVisibility(View.VISIBLE);
+            mScreenshotSelectorView.requestFocus();
         });
     }
 
@@ -326,8 +324,8 @@
 
         mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
         ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation();
-        ValueAnimator screenshotFadeOutAnim = createScreenshotDropOutAnimation(w, h,
-                statusBarVisible, navBarVisible);
+        ValueAnimator screenshotFadeOutAnim =
+                createScreenshotDropOutAnimation(w, h, statusBarVisible, navBarVisible);
         mScreenshotAnimation = new AnimatorSet();
         mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim);
         mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
@@ -336,13 +334,14 @@
                 // Save the screenshot once we have a bit of time now
                 saveScreenshotInWorkerThread(finisher, new GlobalScreenshot.ActionsReadyListener() {
                     @Override
-                    void onActionsReady(Uri uri, List<Notification.Action> actions) {
+                    void onActionsReady(Uri uri, List<Notification.Action> smartActions,
+                            List<Notification.Action> actions) {
                         if (uri == null) {
                             mNotificationsController.notifyScreenshotError(
                                     R.string.screenshot_failed_to_capture_text);
                         } else {
-                            mNotificationsController.showScreenshotActionsNotification(
-                                    uri, actions);
+                            mNotificationsController
+                                    .showScreenshotActionsNotification(uri, smartActions, actions);
                         }
                     }
                 });
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 6bad15c..e6082dd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -65,7 +65,6 @@
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
 import java.util.Objects;
@@ -90,6 +89,7 @@
     private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
     private final String mScreenshotId;
     private final boolean mSmartActionsEnabled;
+    private final boolean mCreateDeleteAction;
     private final Random mRandom = new Random();
 
     SaveImageInBackgroundTask(Context context, GlobalScreenshot.SaveImageInBackgroundData data) {
@@ -102,6 +102,8 @@
         mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate);
         mScreenshotId = String.format(SCREENSHOT_ID_TEMPLATE, UUID.randomUUID());
 
+        mCreateDeleteAction = data.createDeleteAction;
+
         // Initialize screenshot notification smart actions provider.
         mSmartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
                 SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, false);
@@ -196,8 +198,20 @@
             }
 
             List<Notification.Action> actions =
-                    populateNotificationActions(mContext, r, uri, smartActionsFuture);
-            mParams.mActionsReadyListener.onActionsReady(uri, actions);
+                    populateNotificationActions(mContext, r, uri);
+            List<Notification.Action> smartActions = new ArrayList<>();
+            if (mSmartActionsEnabled) {
+                int timeoutMs = DeviceConfig.getInt(
+                        DeviceConfig.NAMESPACE_SYSTEMUI,
+                        SystemUiDeviceConfigFlags.SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS,
+                        1000);
+                smartActions.addAll(buildSmartActions(
+                        ScreenshotSmartActions.getSmartActions(
+                                mScreenshotId, smartActionsFuture, timeoutMs,
+                                mSmartActionsProvider),
+                        mContext));
+            }
+            mParams.mActionsReadyListener.onActionsReady(uri, smartActions, actions);
             mParams.imageUri = uri;
             mParams.image = null;
             mParams.errorMsgResId = 0;
@@ -207,7 +221,7 @@
             Slog.e(TAG, "unable to save screenshot", e);
             mParams.clearImage();
             mParams.errorMsgResId = R.string.screenshot_failed_to_save_text;
-            mParams.mActionsReadyListener.onActionsReady(null, null);
+            mParams.mActionsReadyListener.onActionsReady(null, null, null);
         }
 
         // Recycle the bitmap data
@@ -228,14 +242,13 @@
         // If we are cancelled while the task is running in the background, we may get null
         // params. The finisher is expected to always be called back, so just use the baked-in
         // params from the ctor in any case.
-        mParams.mActionsReadyListener.onActionsReady(null, null);
+        mParams.mActionsReadyListener.onActionsReady(null, null, null);
         mParams.finisher.accept(null);
         mParams.clearImage();
     }
 
     @VisibleForTesting
-    List<Notification.Action> populateNotificationActions(Context context, Resources r, Uri uri,
-            CompletableFuture<List<Notification.Action>> smartActionsFuture) {
+    List<Notification.Action> populateNotificationActions(Context context, Resources r, Uri uri) {
         // Note: Both the share and edit actions are proxied through ActionProxyReceiver in
         // order to do some common work like dismissing the keyguard and sending
         // closeSystemWindows
@@ -260,6 +273,8 @@
         // by setting the (otherwise unused) request code to the current user id.
         int requestCode = context.getUserId();
 
+        ArrayList<Notification.Action> actions = new ArrayList<>();
+
         PendingIntent chooserAction = PendingIntent.getBroadcast(context, requestCode,
                 new Intent(context, GlobalScreenshot.TargetChosenReceiver.class),
                 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
@@ -282,6 +297,7 @@
         Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
                 Icon.createWithResource(r, R.drawable.ic_screenshot_share),
                 r.getString(com.android.internal.R.string.share), shareAction);
+        actions.add(shareActionBuilder.build());
 
         // Create an edit intent, if a specific package is provided as the editor, then
         // launch that directly
@@ -309,31 +325,21 @@
         Notification.Action.Builder editActionBuilder = new Notification.Action.Builder(
                 Icon.createWithResource(r, R.drawable.ic_screenshot_edit),
                 r.getString(com.android.internal.R.string.screenshot_edit), editAction);
+        actions.add(editActionBuilder.build());
 
-        // Create a delete action for the notification
-        PendingIntent deleteAction = PendingIntent.getBroadcast(context, requestCode,
-                new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
-                        .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString())
-                        .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
-                        .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
-                                mSmartActionsEnabled),
-                PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
-        Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
-                Icon.createWithResource(r, R.drawable.ic_screenshot_delete),
-                r.getString(com.android.internal.R.string.delete), deleteAction);
-
-        ArrayList<Notification.Action> actions = new ArrayList<>(
-                Arrays.asList(shareActionBuilder.build(), editActionBuilder.build(),
-                        deleteActionBuilder.build()));
-        if (mSmartActionsEnabled) {
-            int timeoutMs = DeviceConfig.getInt(
-                    DeviceConfig.NAMESPACE_SYSTEMUI,
-                    SystemUiDeviceConfigFlags.SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS,
-                    1000);
-            actions.addAll(buildSmartActions(
-                    ScreenshotSmartActions.getSmartActions(
-                            mScreenshotId, smartActionsFuture, timeoutMs, mSmartActionsProvider),
-                    context));
+        if (mCreateDeleteAction) {
+            // Create a delete action for the notification
+            PendingIntent deleteAction = PendingIntent.getBroadcast(context, requestCode,
+                    new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
+                            .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString())
+                            .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
+                            .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
+                                    mSmartActionsEnabled),
+                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
+            Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
+                    Icon.createWithResource(r, R.drawable.ic_screenshot_delete),
+                    r.getString(com.android.internal.R.string.delete), deleteAction);
+            actions.add(deleteActionBuilder.build());
         }
         return actions;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
index 6edacd1..44b20e5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
@@ -17,9 +17,11 @@
 package com.android.systemui.screenshot;
 
 import android.annotation.ColorInt;
+import android.app.PendingIntent;
 import android.content.Context;
 import android.graphics.drawable.Icon;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
@@ -31,6 +33,8 @@
  */
 public class ScreenshotActionChip extends LinearLayout {
 
+    private static final String TAG = "ScreenshotActionChip";
+
     private ImageView mIcon;
     private TextView mText;
     private @ColorInt int mIconColor;
@@ -47,11 +51,11 @@
         this(context, attrs, defStyleAttr, 0);
     }
 
-    public ScreenshotActionChip(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
+    public ScreenshotActionChip(
+            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
-        mIconColor = context.getColor(R.color.global_screenshot_button_text);
+        mIconColor = context.getColor(R.color.global_screenshot_button_icon);
     }
 
     @Override
@@ -70,4 +74,15 @@
     void setText(CharSequence text) {
         mText.setText(text);
     }
+
+    void setPendingIntent(PendingIntent intent, Runnable finisher) {
+        setOnClickListener(v -> {
+            try {
+                intent.send();
+                finisher.run();
+            } catch (PendingIntent.CanceledException e) {
+                Log.e(TAG, "Intent cancelled", e);
+            }
+        });
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
index 42fca94..811a8d9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
@@ -190,10 +190,14 @@
      */
     public void showScreenshotActionsNotification(
             Uri imageUri,
+            List<Notification.Action> smartActions,
             List<Notification.Action> actions) {
         for (Notification.Action action : actions) {
             mNotificationBuilder.addAction(action);
         }
+        for (Notification.Action smartAction : smartActions) {
+            mNotificationBuilder.addAction(smartAction);
+        }
 
         // Create the intent to show the screenshot in gallery
         Intent launchIntent = new Intent(Intent.ACTION_VIEW);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 4f045d5..9570b5a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -107,6 +107,7 @@
     @Override
     public boolean onUnbind(Intent intent) {
         if (mScreenshot != null) mScreenshot.stopScreenshot();
+        // TODO (mkephart) remove once notifications flow is fully deprecated
         if (mScreenshotLegacy != null) mScreenshotLegacy.stopScreenshot();
         return true;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
new file mode 100644
index 0000000..083fbc9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar
+
+import android.app.ActivityManager
+import android.content.res.Resources
+import android.os.SystemProperties
+import android.util.MathUtils
+import android.view.SurfaceControl
+import android.view.ViewRootImpl
+import com.android.internal.util.IndentingPrintWriter
+import com.android.systemui.DumpController
+import com.android.systemui.Dumpable
+import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Main
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class BlurUtils @Inject constructor(
+    @Main private val resources: Resources,
+    val dumpController: DumpController
+) : Dumpable {
+    private val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius)
+    private val maxBlurRadius = resources.getDimensionPixelSize(R.dimen.max_window_blur_radius)
+    private val blurSysProp = SystemProperties
+            .getBoolean("ro.surface_flinger.supports_background_blur", false)
+
+    init {
+        dumpController.registerDumpable(this)
+    }
+
+    /**
+     * Translates a ratio from 0 to 1 to a blur radius in pixels.
+     */
+    fun radiusForRatio(ratio: Float): Int {
+        if (ratio == 0f) {
+            return 0
+        }
+        return MathUtils.lerp(minBlurRadius.toFloat(), maxBlurRadius.toFloat(), ratio).toInt()
+    }
+
+    /**
+     * Applies background blurs to a {@link ViewRootImpl}.
+     *
+     * @param viewRootImpl The window root.
+     * @param radius blur radius in pixels.
+     */
+    fun applyBlur(viewRootImpl: ViewRootImpl?, radius: Int) {
+        if (viewRootImpl == null || !supportsBlursOnWindows()) {
+            return
+        }
+        SurfaceControl.Transaction().use {
+            it.setBackgroundBlurRadius(viewRootImpl.surfaceControl, radius)
+            it.apply()
+        }
+    }
+
+    /**
+     * If this device can render blurs.
+     *
+     * @see android.view.SurfaceControl.Transaction#setBackgroundBlurRadius(SurfaceControl, int)
+     * @return {@code true} when supported.
+     */
+    fun supportsBlursOnWindows(): Boolean {
+        return blurSysProp && ActivityManager.isHighEndGfx()
+    }
+
+    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+        IndentingPrintWriter(pw, "  ").use {
+            it.println("BlurUtils:")
+            it.increaseIndent()
+            it.println("minBlurRadius: $minBlurRadius")
+            it.println("maxBlurRadius: $maxBlurRadius")
+            it.println("blurSysProp: $blurSysProp")
+            it.println("supportsBlursOnWindows: ${supportsBlursOnWindows()}")
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWindowBlurController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWindowBlurController.kt
new file mode 100644
index 0000000..2e72163
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWindowBlurController.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar
+
+import android.content.res.Resources
+import android.view.View
+import com.android.internal.util.IndentingPrintWriter
+import com.android.systemui.DumpController
+import com.android.systemui.Dumpable
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.statusbar.phone.PanelExpansionListener
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * Controller responsible for statusbar window blur.
+ */
+@Singleton
+class StatusBarWindowBlurController @Inject constructor(
+    @Main private val resources: Resources,
+    private val statusBarStateController: SysuiStatusBarStateController,
+    private val blurUtils: BlurUtils,
+    dumpController: DumpController
+) : PanelExpansionListener, Dumpable {
+
+    lateinit var root: View
+    private var blurRadius = 0
+
+    init {
+        dumpController.registerDumpable(this)
+    }
+
+    override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) {
+        val newBlur = if (statusBarStateController.state == StatusBarState.SHADE)
+            blurUtils.radiusForRatio(expansion)
+        else
+            0
+
+        if (blurRadius == newBlur) {
+            return
+        }
+        blurRadius = newBlur
+        blurUtils.applyBlur(root.viewRootImpl, blurRadius)
+    }
+
+    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+        IndentingPrintWriter(pw, "  ").use {
+            it.println("StatusBarWindowBlurController:")
+            it.increaseIndent()
+            it.println("blurRadius: $blurRadius")
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index c691a35..ab1c8ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -18,6 +18,7 @@
 
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 
+import android.annotation.Nullable;
 import android.app.StatusBarManager;
 import android.graphics.RectF;
 import android.hardware.display.AmbientDisplayConfiguration;
@@ -44,6 +45,7 @@
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.StatusBarWindowBlurController;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -79,6 +81,7 @@
     private final CommandQueue mCommandQueue;
     private final NotificationShadeWindowView mView;
     private final ShadeController mShadeController;
+    private final StatusBarWindowBlurController mBlurController;
 
     private GestureDetector mGestureDetector;
     private View mBrightnessMirror;
@@ -120,6 +123,7 @@
             CommandQueue commandQueue,
             ShadeController shadeController,
             DockManager dockManager,
+            @Nullable StatusBarWindowBlurController blurController,
             NotificationShadeWindowView statusBarWindowView,
             NotificationPanelViewController notificationPanelViewController) {
         mInjectionInflationController = injectionInflationController;
@@ -141,6 +145,7 @@
         mShadeController = shadeController;
         mDockManager = dockManager;
         mNotificationPanelViewController = notificationPanelViewController;
+        mBlurController = blurController;
 
         // This view is not part of the newly inflated expanded status bar.
         mBrightnessMirror = mView.findViewById(R.id.brightness_mirror);
@@ -383,6 +388,11 @@
                 new DragDownHelper(
                         mView.getContext(), mView, expandHelperCallback,
                         dragDownCallback, mFalsingManager));
+
+        if (mBlurController != null) {
+            mBlurController.setRoot(mView);
+            mNotificationPanelViewController.addExpansionListener(mBlurController);
+        }
     }
 
     public NotificationShadeWindowView getView() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
index 2c7cee3..aeb31e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
@@ -169,11 +169,11 @@
         data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
         data.finisher = null;
         data.mActionsReadyListener = null;
+        data.createDeleteAction = true;
         SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data);
         List<Notification.Action> actions = task.populateNotificationActions(
                 mContext, mContext.getResources(),
-                Uri.parse("Screenshot_123.png"),
-                CompletableFuture.completedFuture(Collections.emptyList()));
+                Uri.parse("Screenshot_123.png"));
 
         Assert.assertEquals(actions.size(), 3);
         boolean isShareFound = false;
@@ -184,7 +184,8 @@
             Assert.assertNotNull(intent);
             Bundle bundle = intent.getExtras();
             Assert.assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_ID));
-            Assert.assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED));
+            Assert.assertTrue(
+                    bundle.containsKey(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED));
 
             if (action.title.equals(GlobalScreenshot.ACTION_TYPE_DELETE)) {
                 isDeleteFound = intent.getAction() == null;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index 9853540..8936a2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -38,6 +38,7 @@
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.StatusBarWindowBlurController;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -79,6 +80,7 @@
     @Mock private DockManager mDockManager;
     @Mock private NotificationPanelViewController mNotificationPanelViewController;
     @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
+    @Mock private StatusBarWindowBlurController mStatusBarWindowBlurController;
 
     @Before
     public void setUp() {
@@ -112,6 +114,7 @@
                 new CommandQueue(mContext),
                 mShadeController,
                 mDockManager,
+                mStatusBarWindowBlurController,
                 mView,
                 mNotificationPanelViewController);
         mController.setupExpandedStatusBar();
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 9ffe89c..b994e6c 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -21,6 +21,7 @@
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
+import android.app.AlarmManager;
 import android.app.IUiModeManager;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -70,10 +71,19 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.time.DateTimeException;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 
+import static android.app.UiModeManager.MODE_NIGHT_AUTO;
+import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
+import static android.app.UiModeManager.MODE_NIGHT_YES;
+import static android.util.TimeUtils.isTimeBetween;
+
 final class UiModeManagerService extends SystemService {
     private static final String TAG = UiModeManager.class.getSimpleName();
     private static final boolean LOG = false;
@@ -90,7 +100,12 @@
     // we use the override auto mode
     // for example: force night mode off in the night time while in auto mode
     private int mNightModeOverride = mNightMode;
-    protected static final String OVERRIDE_NIGHT_MODE = Secure.UI_NIGHT_MODE + "_override";
+    private final LocalTime DEFAULT_CUSTOM_NIGHT_START_TIME = LocalTime.of(22, 0);
+    private final LocalTime DEFAULT_CUSTOM_NIGHT_END_TIME = LocalTime.of(6, 0);
+    private LocalTime mCustomAutoNightModeStartMilliseconds = DEFAULT_CUSTOM_NIGHT_START_TIME;
+    private LocalTime mCustomAutoNightModeEndMilliseconds = DEFAULT_CUSTOM_NIGHT_END_TIME;
+
+    protected static final String OVERRIDE_NIGHT_MODE = Secure.UI_NIGHT_MODE_OVERRIDE;
 
     private Map<Integer, String> mCarModePackagePriority = new HashMap<>();
     private boolean mCarModeEnabled = false;
@@ -131,6 +146,8 @@
     private NotificationManager mNotificationManager;
     private StatusBarManager mStatusBarManager;
     private WindowManagerInternal mWindowManager;
+    private AlarmManager mAlarmManager;
+    private PowerManager mPowerManager;
 
     private PowerManager.WakeLock mWakeLock;
 
@@ -141,14 +158,16 @@
     }
 
     @VisibleForTesting
-    protected UiModeManagerService(Context context, WindowManagerInternal wm,
-                                   PowerManager.WakeLock wl, TwilightManager tm,
+    protected UiModeManagerService(Context context, WindowManagerInternal wm, AlarmManager am,
+                                   PowerManager pm, PowerManager.WakeLock wl, TwilightManager tm,
                                    boolean setupWizardComplete) {
         super(context);
         mWindowManager = wm;
         mWakeLock = wl;
         mTwilightManager = tm;
         mSetupWizardComplete = setupWizardComplete;
+        mAlarmManager = am;
+        mPowerManager = pm;
     }
 
     private static Intent buildHomeIntent(String category) {
@@ -237,6 +256,21 @@
         }
     };
 
+    private final BroadcastReceiver mOnTimeChangedHandler = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            synchronized (mLock) {
+                updateCustomTimeLocked();
+            }
+        }
+    };
+
+    private final AlarmManager.OnAlarmListener mCustomTimeListener = () -> {
+        synchronized (mLock) {
+            updateCustomTimeLocked();
+        }
+    };
+
     private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
         @Override
         public void onVrStateChanged(boolean enabled) {
@@ -270,8 +304,9 @@
         public void onChange(boolean selfChange, Uri uri) {
             int mode = Secure.getIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE,
                     mNightMode, 0);
-            mode = mode == UiModeManager.MODE_NIGHT_AUTO
-                    ? UiModeManager.MODE_NIGHT_YES : UiModeManager.MODE_NIGHT_NO;
+            if (mode == MODE_NIGHT_AUTO || mode == MODE_NIGHT_CUSTOM) {
+                mode = MODE_NIGHT_YES;
+            }
             SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, Integer.toString(mode));
         }
     };
@@ -287,10 +322,11 @@
     public void onStart() {
         final Context context = getContext();
 
-        final PowerManager powerManager =
+        mPowerManager =
                 (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-        mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
+        mWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
         mWindowManager = LocalServices.getService(WindowManagerInternal.class);
+        mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
 
         // If setup isn't complete for this user listen for completion so we can unblock
         // being able to send a night mode configuration change event
@@ -387,6 +423,16 @@
                 Secure.USER_SETUP_COMPLETE, 0, UserHandle.getCallingUserId()) == 1;
     }
 
+    private void updateCustomTimeLocked() {
+        if (mNightMode != MODE_NIGHT_CUSTOM) return;
+        if (shouldApplyAutomaticChangesImmediately()) {
+            updateLocked(0, 0);
+        } else {
+            registerScreenOffEvent();
+        }
+        scheduleNextCustomTimeListener();
+    }
+
     /**
      * Updates the night mode setting in Settings.Global and returns if the value was successfully
      * changed.
@@ -404,9 +450,19 @@
                     Secure.UI_NIGHT_MODE, defaultNightMode, userId);
             mNightModeOverride = Secure.getIntForUser(context.getContentResolver(),
                     OVERRIDE_NIGHT_MODE, defaultNightMode, userId);
+            mCustomAutoNightModeStartMilliseconds = LocalTime.ofNanoOfDay(
+                    Secure.getLongForUser(context.getContentResolver(),
+                    Secure.DARK_THEME_CUSTOM_START_TIME,
+                    DEFAULT_CUSTOM_NIGHT_START_TIME.toNanoOfDay() / 1000L, userId) * 1000);
+            mCustomAutoNightModeEndMilliseconds = LocalTime.ofNanoOfDay(
+                    Secure.getLongForUser(context.getContentResolver(),
+                    Secure.DARK_THEME_CUSTOM_END_TIME,
+                    DEFAULT_CUSTOM_NIGHT_END_TIME.toNanoOfDay() / 1000L, userId) * 1000);
         } else {
             mNightMode = defaultNightMode;
             mNightModeOverride = defaultNightMode;
+            mCustomAutoNightModeEndMilliseconds = DEFAULT_CUSTOM_NIGHT_END_TIME;
+            mCustomAutoNightModeStartMilliseconds = DEFAULT_CUSTOM_NIGHT_START_TIME;
         }
 
         return oldNightMode != mNightMode;
@@ -419,6 +475,10 @@
         getContext().registerReceiver(mOnScreenOffHandler, intentFilter);
     }
 
+    private void cancelCustomAlarm() {
+        mAlarmManager.cancel(mCustomTimeListener);
+    }
+
     private void unregisterScreenOffEvent() {
         mWaitForScreenOff = false;
         try {
@@ -428,6 +488,21 @@
         }
     }
 
+    private void registerTimeChangeEvent() {
+        final IntentFilter intentFilter =
+                new IntentFilter(Intent.ACTION_TIME_CHANGED);
+        intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+        getContext().registerReceiver(mOnTimeChangedHandler, intentFilter);
+    }
+
+    private void unregisterTimeChangeEvent() {
+        try {
+            getContext().unregisterReceiver(mOnTimeChangedHandler);
+        } catch (IllegalArgumentException e) {
+            // we ignore this exception if the receiver is unregistered already.
+        }
+    }
+
     private final IUiModeManager.Stub mService = new IUiModeManager.Stub() {
         @Override
         public void enableCarMode(@UiModeManager.EnableCarMode int flags,
@@ -537,7 +612,8 @@
             switch (mode) {
                 case UiModeManager.MODE_NIGHT_NO:
                 case UiModeManager.MODE_NIGHT_YES:
-                case UiModeManager.MODE_NIGHT_AUTO:
+                case MODE_NIGHT_AUTO:
+                case MODE_NIGHT_CUSTOM:
                     break;
                 default:
                     throw new IllegalArgumentException("Unknown mode: " + mode);
@@ -548,8 +624,9 @@
             try {
                 synchronized (mLock) {
                     if (mNightMode != mode) {
-                        if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+                        if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
                             unregisterScreenOffEvent();
+                            cancelCustomAlarm();
                         }
 
                         mNightMode = mode;
@@ -559,7 +636,9 @@
                             persistNightMode(user);
                         }
                         // on screen off will update configuration instead
-                        if (mNightMode != UiModeManager.MODE_NIGHT_AUTO || mCar) {
+                        if ((mNightMode != MODE_NIGHT_AUTO && mNightMode != MODE_NIGHT_CUSTOM)
+                                || shouldApplyAutomaticChangesImmediately()) {
+                            unregisterScreenOffEvent();
                             updateLocked(0, 0);
                         } else {
                             registerScreenOffEvent();
@@ -610,7 +689,7 @@
                 final int user = UserHandle.getCallingUserId();
                 final long ident = Binder.clearCallingIdentity();
                 try {
-                    if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+                    if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
                         unregisterScreenOffEvent();
                         mNightModeOverride = active
                                 ? UiModeManager.MODE_NIGHT_YES : UiModeManager.MODE_NIGHT_NO;
@@ -630,8 +709,74 @@
                 }
             }
         }
+
+        @Override
+        public long getCustomNightModeStart() {
+            return mCustomAutoNightModeStartMilliseconds.toNanoOfDay() / 1000;
+        }
+
+        @Override
+        public void setCustomNightModeStart(long time) {
+            if (isNightModeLocked() && getContext().checkCallingOrSelfPermission(
+                    android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
+                    != PackageManager.PERMISSION_GRANTED) {
+                Slog.e(TAG, "Set custom time start, requires MODIFY_DAY_NIGHT_MODE permission");
+                return;
+            }
+            final int user = UserHandle.getCallingUserId();
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000);
+                if (newTime == null) return;
+                mCustomAutoNightModeStartMilliseconds = newTime;
+                persistNightMode(user);
+                onCustomTimeUpdated(user);
+            } catch (DateTimeException e) {
+                unregisterScreenOffEvent();
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override
+        public long getCustomNightModeEnd() {
+            return mCustomAutoNightModeEndMilliseconds.toNanoOfDay() / 1000;
+        }
+
+        @Override
+        public void setCustomNightModeEnd(long time) {
+            if (isNightModeLocked() && getContext().checkCallingOrSelfPermission(
+                    android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
+                    != PackageManager.PERMISSION_GRANTED) {
+                Slog.e(TAG, "Set custom time end, requires MODIFY_DAY_NIGHT_MODE permission");
+                return;
+            }
+            final int user = UserHandle.getCallingUserId();
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000);
+                if (newTime == null) return;
+                mCustomAutoNightModeEndMilliseconds = newTime;
+                onCustomTimeUpdated(user);
+            } catch (DateTimeException e) {
+                unregisterScreenOffEvent();
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
     };
 
+    private void onCustomTimeUpdated(int user) {
+        persistNightMode(user);
+        if (mNightMode != MODE_NIGHT_CUSTOM) return;
+        if (shouldApplyAutomaticChangesImmediately()) {
+            unregisterScreenOffEvent();
+            updateLocked(0, 0);
+        } else {
+            registerScreenOffEvent();
+        }
+    }
+
     void dumpImpl(PrintWriter pw) {
         synchronized (mLock) {
             pw.println("Current UI Mode Service state:");
@@ -677,7 +822,6 @@
                 mTwilightManager = getLocalService(TwilightManager.class);
                 mSystemReady = true;
                 mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
-                updateComputedNightModeLocked();
                 registerVrStateListener();
                 updateLocked(0, 0);
             }
@@ -838,6 +982,12 @@
                 Secure.UI_NIGHT_MODE, mNightMode, user);
         Secure.putIntForUser(getContext().getContentResolver(),
                 OVERRIDE_NIGHT_MODE, mNightModeOverride, user);
+        Secure.putLongForUser(getContext().getContentResolver(),
+                Secure.DARK_THEME_CUSTOM_START_TIME,
+                mCustomAutoNightModeStartMilliseconds.toNanoOfDay() / 1000, user);
+        Secure.putLongForUser(getContext().getContentResolver(),
+                Secure.DARK_THEME_CUSTOM_END_TIME,
+                mCustomAutoNightModeEndMilliseconds.toNanoOfDay() / 1000, user);
     }
 
     private void updateConfigurationLocked() {
@@ -856,13 +1006,16 @@
             uiMode = Configuration.UI_MODE_TYPE_VR_HEADSET;
         }
 
-        if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+        if (mNightMode == MODE_NIGHT_AUTO) {
+            boolean activateNightMode = mComputedNightMode;
             if (mTwilightManager != null) {
                 mTwilightManager.registerListener(mTwilightListener, mHandler);
+                final TwilightState lastState = mTwilightManager.getLastTwilightState();
+                activateNightMode = lastState == null ? mComputedNightMode : lastState.isNight();
             }
-            updateComputedNightModeLocked();
-            uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
-                    : Configuration.UI_MODE_NIGHT_NO;
+            
+            updateComputedNightModeLocked(activateNightMode);
+            uiMode = getComputedUiModeConfiguration(uiMode);
         } else {
             if (mTwilightManager != null) {
                 mTwilightManager.unregisterListener(mTwilightListener);
@@ -870,6 +1023,16 @@
             uiMode |= mNightMode << 4;
         }
 
+        if (mNightMode == MODE_NIGHT_CUSTOM) {
+            registerTimeChangeEvent();
+            final boolean activate = computeCustomNightMode();
+            updateComputedNightModeLocked(activate);
+            scheduleNextCustomTimeListener();
+            uiMode = getComputedUiModeConfiguration(uiMode);
+        } else {
+            unregisterTimeChangeEvent();
+        }
+
         // Override night mode in power save mode if not in car mode
         if (mPowerSave && !mCarModeEnabled) {
             uiMode &= ~Configuration.UI_MODE_NIGHT_NO;
@@ -885,11 +1048,26 @@
         }
 
         mCurUiMode = uiMode;
-        if (!mHoldingConfiguration || !mWaitForScreenOff) {
+        if (!mHoldingConfiguration && !mWaitForScreenOff) {
             mConfiguration.uiMode = uiMode;
         }
     }
 
+    @UiModeManager.NightMode
+    private int getComputedUiModeConfiguration(@UiModeManager.NightMode int uiMode) {
+        uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
+                : Configuration.UI_MODE_NIGHT_NO;
+        uiMode &= mComputedNightMode ? ~Configuration.UI_MODE_NIGHT_NO
+                : ~Configuration.UI_MODE_NIGHT_YES;
+        return uiMode;
+    }
+
+    private boolean computeCustomNightMode() {
+        return isTimeBetween(LocalTime.now(),
+                mCustomAutoNightModeStartMilliseconds,
+                mCustomAutoNightModeEndMilliseconds);
+    }
+
     private void applyConfigurationExternallyLocked() {
         if (mSetUiMode != mConfiguration.uiMode) {
             mSetUiMode = mConfiguration.uiMode;
@@ -899,10 +1077,34 @@
                 ActivityTaskManager.getService().updateConfiguration(mConfiguration);
             } catch (RemoteException e) {
                 Slog.w(TAG, "Failure communicating with activity manager", e);
+            } catch (SecurityException e) {
+                Slog.e(TAG, "Activity does not have the ", e);
             }
         }
     }
 
+    private boolean shouldApplyAutomaticChangesImmediately() {
+        return mCar || !mPowerManager.isInteractive();
+    }
+
+    private void scheduleNextCustomTimeListener() {
+        cancelCustomAlarm();
+        LocalDateTime now = LocalDateTime.now();
+        final boolean active = computeCustomNightMode();
+        final LocalDateTime next = active
+                ? getDateTimeAfter(mCustomAutoNightModeEndMilliseconds, now)
+                : getDateTimeAfter(mCustomAutoNightModeStartMilliseconds, now);
+        final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
+        mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, mCustomTimeListener, null);
+    }
+
+    private LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
+        final LocalDateTime ldt = LocalDateTime.of(compareTime.toLocalDate(), localTime);
+
+        // Check if the local time has passed, if so return the same time tomorrow.
+        return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
+    }
+
     void updateLocked(int enableFlags, int disableFlags) {
         String action = null;
         String oldAction = null;
@@ -1133,26 +1335,21 @@
         }
     }
 
-    private void updateComputedNightModeLocked() {
-        if (mTwilightManager != null) {
-            TwilightState state = mTwilightManager.getLastTwilightState();
-            if (state != null) {
-                mComputedNightMode = state.isNight();
-            }
-            if (mNightModeOverride == UiModeManager.MODE_NIGHT_YES && !mComputedNightMode) {
-                mComputedNightMode = true;
-                return;
-            }
-            if (mNightModeOverride == UiModeManager.MODE_NIGHT_NO && mComputedNightMode) {
-                mComputedNightMode = false;
-                return;
-            }
-
-            mNightModeOverride = mNightMode;
-            final int user = UserHandle.getCallingUserId();
-            Secure.putIntForUser(getContext().getContentResolver(),
-                    OVERRIDE_NIGHT_MODE, mNightModeOverride, user);
+    private void updateComputedNightModeLocked(boolean activate) {
+        mComputedNightMode = activate;
+        if (mNightModeOverride == UiModeManager.MODE_NIGHT_YES && !mComputedNightMode) {
+            mComputedNightMode = true;
+            return;
         }
+        if (mNightModeOverride == UiModeManager.MODE_NIGHT_NO && mComputedNightMode) {
+            mComputedNightMode = false;
+            return;
+        }
+
+        mNightModeOverride = mNightMode;
+        final int user = UserHandle.getCallingUserId();
+        Secure.putIntForUser(getContext().getContentResolver(),
+                OVERRIDE_NIGHT_MODE, mNightModeOverride, user);
     }
 
     private void registerVrStateListener() {
@@ -1174,6 +1371,7 @@
         public static final String NIGHT_MODE_STR_YES = "yes";
         public static final String NIGHT_MODE_STR_NO = "no";
         public static final String NIGHT_MODE_STR_AUTO = "auto";
+        public static final String NIGHT_MODE_STR_CUSTOM = "custom";
         public static final String NIGHT_MODE_STR_UNKNOWN = "unknown";
         private final IUiModeManager mInterface;
 
@@ -1246,6 +1444,8 @@
                     return NIGHT_MODE_STR_NO;
                 case UiModeManager.MODE_NIGHT_AUTO:
                     return NIGHT_MODE_STR_AUTO;
+                case MODE_NIGHT_CUSTOM:
+                    return NIGHT_MODE_STR_CUSTOM;
                 default:
                     return NIGHT_MODE_STR_UNKNOWN;
             }
@@ -1259,6 +1459,8 @@
                     return UiModeManager.MODE_NIGHT_NO;
                 case NIGHT_MODE_STR_AUTO:
                     return UiModeManager.MODE_NIGHT_AUTO;
+                case NIGHT_MODE_STR_CUSTOM:
+                    return UiModeManager.MODE_NIGHT_CUSTOM;
                 default:
                     return -1;
             }
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 4a6e63f..0a8e70c 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -87,6 +87,7 @@
         DeviceConfig.NAMESPACE_RUNTIME_NATIVE,
         DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
         DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+        DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
     };
 
     private final String[] mGlobalSettings;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index f3a2e70..afc3d91 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -54,6 +54,7 @@
 import android.content.Context;
 import android.content.IIntentReceiver;
 import android.content.Intent;
+import android.content.PermissionChecker;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
@@ -1756,7 +1757,7 @@
                 // We require full access, sucks to be you.
                 allow = false;
             } else if (canInteractWithAcrossProfilesPermission(
-                    allowMode, isSameProfileGroup, callingPid, callingUid)) {
+                    allowMode, isSameProfileGroup, callingPid, callingUid, callerPackage)) {
                 allow = true;
             } else if (mInjector.checkComponentPermission(INTERACT_ACROSS_USERS, callingPid,
                     callingUid, -1, true) != PackageManager.PERMISSION_GRANTED) {
@@ -1821,16 +1822,21 @@
     }
 
     private boolean canInteractWithAcrossProfilesPermission(
-            int allowMode, boolean isSameProfileGroup, int callingPid, int callingUid) {
+            int allowMode, boolean isSameProfileGroup, int callingPid, int callingUid,
+            String callingPackage) {
         if (allowMode != ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
             return false;
         }
         if (!isSameProfileGroup) {
             return false;
         }
-        return mInjector.checkComponentPermission(
-                INTERACT_ACROSS_PROFILES, callingPid, callingUid, /*owningUid= */-1,
-                /*exported= */true) == PackageManager.PERMISSION_GRANTED;
+        return  PermissionChecker.PERMISSION_GRANTED
+                == PermissionChecker.checkPermissionForPreflight(
+                        mInjector.getContext(),
+                        INTERACT_ACROSS_PROFILES,
+                        callingPid,
+                        callingUid,
+                        callingPackage);
     }
 
     int unsafeConvertIncomingUser(@UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 385d84a..767a1c0 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -844,20 +844,22 @@
 
     private static final class ToastRecord
     {
-        final int pid;
-        final String pkg;
-        final ITransientNotification callback;
-        int duration;
-        int displayId;
-        Binder token;
+        public final int pid;
+        public final String pkg;
+        public final IBinder token;
+        public final ITransientNotification callback;
+        public int duration;
+        public int displayId;
+        public Binder windowToken;
 
-        ToastRecord(int pid, String pkg, ITransientNotification callback, int duration,
-                Binder token, int displayId) {
+        ToastRecord(int pid, String pkg, IBinder token, ITransientNotification callback,
+                int duration, Binder windowToken, int displayId) {
             this.pid = pid;
             this.pkg = pkg;
+            this.token = token;
             this.callback = callback;
             this.duration = duration;
-            this.token = token;
+            this.windowToken = windowToken;
             this.displayId = displayId;
         }
 
@@ -876,8 +878,10 @@
             return "ToastRecord{"
                 + Integer.toHexString(System.identityHashCode(this))
                 + " pkg=" + pkg
+                + " token=" + token
                 + " callback=" + callback
-                + " duration=" + duration;
+                + " duration=" + duration
+                + "}";
         }
     }
 
@@ -2559,26 +2563,27 @@
         // ============================================================================
 
         @Override
-        public void enqueueTextToast(String pkg, ITransientNotification callback, int duration,
-                int displayId) {
-            enqueueToast(pkg, callback, duration, displayId, false);
+        public void enqueueTextToast(String pkg, IBinder token, ITransientNotification callback,
+                int duration, int displayId) {
+            enqueueToast(pkg, token, callback, duration, displayId, false);
         }
 
         @Override
-        public void enqueueToast(String pkg, ITransientNotification callback, int duration,
-                int displayId) {
-            enqueueToast(pkg, callback, duration, displayId, true);
+        public void enqueueToast(String pkg, IBinder token, ITransientNotification callback,
+                int duration, int displayId) {
+            enqueueToast(pkg, token, callback, duration, displayId, true);
         }
 
-        private void enqueueToast(String pkg, ITransientNotification callback, int duration,
-                int displayId, boolean isCustomToast) {
+        private void enqueueToast(String pkg, IBinder token, ITransientNotification callback,
+                int duration, int displayId, boolean isCustomToast) {
             if (DBG) {
                 Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
                         + " duration=" + duration + " displayId=" + displayId);
             }
 
-            if (pkg == null || callback == null) {
-                Slog.e(TAG, "Not enqueuing toast. pkg=" + pkg + " callback=" + callback);
+            if (pkg == null || callback == null || token == null) {
+                Slog.e(TAG, "Not enqueuing toast. pkg=" + pkg + " callback=" + callback + " token="
+                        + token);
                 return ;
             }
 
@@ -2636,7 +2641,7 @@
                 long callingId = Binder.clearCallingIdentity();
                 try {
                     ToastRecord record;
-                    int index = indexOfToastLocked(pkg, callback);
+                    int index = indexOfToastLocked(pkg, token);
                     // If it's already in the queue, we update it in place, we don't
                     // move it to the end of the queue.
                     if (index >= 0) {
@@ -2661,10 +2666,10 @@
                             }
                         }
 
-                        Binder token = new Binder();
-                        mWindowManagerInternal.addWindowToken(token, TYPE_TOAST, displayId);
-                        record = new ToastRecord(callingPid, pkg, callback, duration, token,
-                                displayId);
+                        Binder windowToken = new Binder();
+                        mWindowManagerInternal.addWindowToken(windowToken, TYPE_TOAST, displayId);
+                        record = new ToastRecord(callingPid, pkg, token, callback, duration,
+                                windowToken, displayId);
                         mToastQueue.add(record);
                         index = mToastQueue.size() - 1;
                         keepProcessAliveIfNeededLocked(callingPid);
@@ -2683,23 +2688,23 @@
         }
 
         @Override
-        public void cancelToast(String pkg, ITransientNotification callback) {
-            Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback);
+        public void cancelToast(String pkg, IBinder token) {
+            Slog.i(TAG, "cancelToast pkg=" + pkg + " token=" + token);
 
-            if (pkg == null || callback == null) {
-                Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback);
+            if (pkg == null || token == null) {
+                Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " token=" + token);
                 return ;
             }
 
             synchronized (mToastQueue) {
                 long callingId = Binder.clearCallingIdentity();
                 try {
-                    int index = indexOfToastLocked(pkg, callback);
+                    int index = indexOfToastLocked(pkg, token);
                     if (index >= 0) {
                         cancelToastLocked(index);
                     } else {
                         Slog.w(TAG, "Toast already cancelled. pkg=" + pkg
-                                + " callback=" + callback);
+                                + " token=" + token);
                     }
                 } finally {
                     Binder.restoreCallingIdentity(callingId);
@@ -2708,17 +2713,17 @@
         }
 
         @Override
-        public void finishToken(String pkg, ITransientNotification callback) {
+        public void finishToken(String pkg, IBinder token) {
             synchronized (mToastQueue) {
                 long callingId = Binder.clearCallingIdentity();
                 try {
-                    int index = indexOfToastLocked(pkg, callback);
+                    int index = indexOfToastLocked(pkg, token);
                     if (index >= 0) {
                         ToastRecord record = mToastQueue.get(index);
-                        finishTokenLocked(record.token, record.displayId);
+                        finishWindowTokenLocked(record.windowToken, record.displayId);
                     } else {
                         Slog.w(TAG, "Toast already killed. pkg=" + pkg
-                                + " callback=" + callback);
+                                + " token=" + token);
                     }
                 } finally {
                     Binder.restoreCallingIdentity(callingId);
@@ -6757,7 +6762,7 @@
         while (record != null) {
             if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
             try {
-                record.callback.show(record.token);
+                record.callback.show(record.windowToken);
                 scheduleDurationReachedLocked(record);
                 return;
             } catch (RemoteException e) {
@@ -6792,7 +6797,7 @@
 
         ToastRecord lastToast = mToastQueue.remove(index);
 
-        mWindowManagerInternal.removeWindowToken(lastToast.token, false /* removeWindows */,
+        mWindowManagerInternal.removeWindowToken(lastToast.windowToken, false /* removeWindows */,
                 lastToast.displayId);
         // We passed 'false' for 'removeWindows' so that the client has time to stop
         // rendering (as hide above is a one-way message), otherwise we could crash
@@ -6810,7 +6815,7 @@
         }
     }
 
-    void finishTokenLocked(IBinder t, int displayId) {
+    void finishWindowTokenLocked(IBinder t, int displayId) {
         mHandler.removeCallbacksAndMessages(t);
         // We pass 'true' for 'removeWindows' to let the WindowManager destroy any
         // remaining surfaces as either the client has called finishToken indicating
@@ -6835,9 +6840,9 @@
 
     private void handleDurationReached(ToastRecord record)
     {
-        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback);
+        if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " token=" + record.token);
         synchronized (mToastQueue) {
-            int index = indexOfToastLocked(record.pkg, record.callback);
+            int index = indexOfToastLocked(record.pkg, record.token);
             if (index >= 0) {
                 cancelToastLocked(index);
             }
@@ -6854,21 +6859,19 @@
 
     private void handleKillTokenTimeout(ToastRecord record)
     {
-        if (DBG) Slog.d(TAG, "Kill Token Timeout token=" + record.token);
+        if (DBG) Slog.d(TAG, "Kill Token Timeout token=" + record.windowToken);
         synchronized (mToastQueue) {
-            finishTokenLocked(record.token, record.displayId);
+            finishWindowTokenLocked(record.windowToken, record.displayId);
         }
     }
 
     @GuardedBy("mToastQueue")
-    int indexOfToastLocked(String pkg, ITransientNotification callback)
-    {
-        IBinder cbak = callback.asBinder();
+    int indexOfToastLocked(String pkg, IBinder token) {
         ArrayList<ToastRecord> list = mToastQueue;
         int len = list.size();
         for (int i=0; i<len; i++) {
             ToastRecord r = list.get(i);
-            if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) {
+            if (r.pkg.equals(pkg) && r.token == token) {
                 return i;
             }
         }
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index bdc1b07..3635004 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -35,6 +35,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.PermissionChecker;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ICrossProfileApps;
 import android.content.pm.IPackageManager;
@@ -114,6 +115,7 @@
 
         final int callerUserId = mInjector.getCallingUserId();
         final int callingUid = mInjector.getCallingUid();
+        final int callingPid = mInjector.getCallingPid();
 
         List<UserHandle> allowedTargetUsers = getTargetUserProfilesUnchecked(
                 callingPackage, callerUserId);
@@ -143,10 +145,13 @@
             // must have the required permission and the users must be in the same profile group
             // in order to launch any of its own activities.
             if (callerUserId != userId) {
-                final int permissionFlag = mInjector.checkComponentPermission(
-                        android.Manifest.permission.INTERACT_ACROSS_PROFILES, callingUid,
-                        -1, true);
-                if (permissionFlag != PackageManager.PERMISSION_GRANTED
+                final int permissionFlag =  PermissionChecker.checkPermissionForPreflight(
+                        mContext,
+                        android.Manifest.permission.INTERACT_ACROSS_PROFILES,
+                        callingPid,
+                        callingUid,
+                        callingPackage);
+                if (permissionFlag != PermissionChecker.PERMISSION_GRANTED
                         || !isSameProfileGroup(callerUserId, userId)) {
                     throw new SecurityException("Attempt to launch activity without required "
                             + android.Manifest.permission.INTERACT_ACROSS_PROFILES + " permission"
@@ -210,12 +215,15 @@
             return false;
         }
         final int callingUid = mInjector.getCallingUid();
+        final int callingPid = mInjector.getCallingPid();
         return isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid)
                 || isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS, callingUid)
-                || isPermissionGranted(Manifest.permission.INTERACT_ACROSS_PROFILES, callingUid)
-                || AppOpsManager.MODE_ALLOWED == getAppOpsService().noteOperation(
-                OP_INTERACT_ACROSS_PROFILES, callingUid, callingPackage, /* featureId= */ null,
-                /*shouldCollectAsyncNotedOp= */false, /*message= */null);
+                || PermissionChecker.checkPermissionForPreflight(
+                        mContext,
+                        Manifest.permission.INTERACT_ACROSS_PROFILES,
+                        callingPid,
+                        callingUid,
+                        callingPackage) == PermissionChecker.PERMISSION_GRANTED;
     }
 
     private boolean isCrossProfilePackageWhitelisted(String packageName) {
@@ -436,6 +444,10 @@
             return Binder.getCallingUid();
         }
 
+        public int getCallingPid() {
+            return Binder.getCallingPid();
+        }
+
         public int getCallingUserId() {
             return UserHandle.getCallingUserId();
         }
@@ -504,6 +516,8 @@
     public interface Injector {
         int getCallingUid();
 
+        int getCallingPid();
+
         int getCallingUserId();
 
         UserHandle getCallingUserHandle();
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 0411e29..6167a50 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -58,6 +58,7 @@
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
 import android.content.Context;
+import android.content.PermissionChecker;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.PermissionGroupInfoFlags;
@@ -4070,8 +4071,13 @@
             return;
         }
         final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, userId);
-        if (isSameProfileGroup
-                && hasPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES)) {
+        if (isSameProfileGroup && PermissionChecker.checkPermissionForPreflight(
+                mContext,
+                android.Manifest.permission.INTERACT_ACROSS_PROFILES,
+                PermissionChecker.PID_UNKNOWN,
+                callingUid,
+                mPackageManagerInt.getPackage(callingUid).getPackageName())
+                == PermissionChecker.PERMISSION_GRANTED) {
             return;
         }
         String errorMessage = buildInvalidCrossUserOrProfilePermissionMessage(
@@ -4349,7 +4355,8 @@
         @Override
         public void enforceCrossUserOrProfilePermission(int callingUid, int userId,
                 boolean requireFullPermission, boolean checkShell, String message) {
-            PermissionManagerService.this.enforceCrossUserOrProfilePermission(callingUid,
+            PermissionManagerService.this.enforceCrossUserOrProfilePermission(
+                    callingUid,
                     userId,
                     requireFullPermission,
                     checkShell,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 0e13e6c..d1c8448 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -31,7 +31,6 @@
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
 import static android.app.ActivityOptions.ANIM_UNDEFINED;
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE;
@@ -1426,7 +1425,7 @@
                 return null;
             }
             ActivityRecord r = token.weakActivity.get();
-            if (r == null || r.getActivityStack() == null) {
+            if (r == null || r.getRootTask() == null) {
                 return null;
             }
             return r;
@@ -2055,19 +2054,17 @@
         }
     }
 
-    /**
-     * @return Stack value from current task, null if there is no task.
-     */
-    ActivityStack getActivityStack() {
-        return task != null ? task.getStack() : null;
+    /** @return Root task of this activity, null if there is no task. */
+    ActivityStack getRootTask() {
+        return task != null ? (ActivityStack) task.getRootTask() : null;
     }
 
-    int getStackId() {
-        return getActivityStack() != null ? getActivityStack().mStackId : INVALID_STACK_ID;
+    int getRootTaskId() {
+        return task != null ? task.getRootTaskId() : INVALID_TASK_ID;
     }
 
     DisplayContent getDisplay() {
-        final ActivityStack stack = getActivityStack();
+        final ActivityStack stack = getRootTask();
         return stack != null ? stack.getDisplay() : null;
     }
 
@@ -2088,7 +2085,7 @@
         mWmService.mWindowPlacerLocked.requestTraversal();
 
         if (changed && task != null && !occludesParent) {
-            getActivityStack().convertActivityToTranslucent(this);
+            getRootTask().convertActivityToTranslucent(this);
         }
         // Always ensure visibility if this activity doesn't occlude parent, so the
         // {@link #returningOptions} of the activity under this one can be applied in
@@ -2123,7 +2120,7 @@
     }
 
     boolean isInStackLocked() {
-        final ActivityStack stack = getActivityStack();
+        final ActivityStack stack = getRootTask();
         return stack != null && stack.isInStackLocked(this) != null;
     }
 
@@ -2226,7 +2223,7 @@
         boolean isCurrentAppLocked =
                 mAtmService.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
         final DisplayContent display = getDisplay();
-        boolean hasPinnedStack = display != null && display.hasPinnedStack();
+        boolean hasPinnedStack = display != null && display.hasPinnedTask();
         // Don't return early if !isNotLocked, since we want to throw an exception if the activity
         // is in an incorrect state
         boolean isNotLockedOrOnKeyguard = !isKeyguardLocked && !isCurrentAppLocked;
@@ -2325,7 +2322,7 @@
             return false;
         }
 
-        final ActivityStack stack = getActivityStack();
+        final ActivityStack stack = getRootTask();
         if (stack == null) {
             Slog.w(TAG, "moveActivityStackToFront: invalid task or stack: activity="
                     + this + " task=" + task);
@@ -2456,7 +2453,7 @@
             return FINISH_RESULT_CANCELLED;
         }
 
-        final ActivityStack stack = getActivityStack();
+        final ActivityStack stack = getRootTask();
         final boolean mayAdjustTop = (isState(RESUMED) || stack.mResumedActivity == null)
                 && stack.isFocusedStackOnDisplay();
         final boolean shouldAdjustGlobalFocus = mayAdjustTop
@@ -2497,7 +2494,7 @@
                     final DisplayContent display = stack.getDisplay();
                     next = display.topRunningActivity();
                     if (next != null) {
-                        display.positionStackAtTop(next.getActivityStack(),
+                        display.positionStackAtTop(next.getRootTask(),
                                 false /* includingParents */, "finish-display-top");
                     }
                 }
@@ -2666,13 +2663,13 @@
         mStackSupervisor.mStoppingActivities.remove(this);
         mStackSupervisor.mGoingToSleepActivities.remove(this);
 
-        final ActivityStack stack = getActivityStack();
+        final ActivityStack stack = getRootTask();
         final DisplayContent display = getDisplay();
         // TODO(b/137329632): Exclude current activity when looking for the next one with
         // DisplayContent#topRunningActivity().
         final ActivityRecord next = display.topRunningActivity();
         final boolean isLastStackOverEmptyHome =
-                next == null && stack.isFocusedStackOnDisplay() && display.getHomeStack() != null;
+                next == null && stack.isFocusedStackOnDisplay() && display.getRootHomeTask() != null;
         if (isLastStackOverEmptyHome) {
             // Don't destroy activity immediately if this is the last activity on the display and
             // the display contains home stack. Although there is no next activity at the moment,
@@ -2824,7 +2821,7 @@
     boolean safelyDestroy(String reason) {
         if (isDestroyable()) {
             if (DEBUG_SWITCH) {
-                final ActivityStack stack = getActivityStack();
+                final ActivityStack stack = getRootTask();
                 Slog.v(TAG_SWITCH, "Safely destroying " + this + " in state " + getState()
                         + " resumed=" + stack.mResumedActivity
                         + " pausing=" + stack.mPausingActivity
@@ -3612,7 +3609,7 @@
     }
 
     final boolean isSleeping() {
-        final ActivityStack stack = getActivityStack();
+        final ActivityStack stack = getRootTask();
         return stack != null ? stack.shouldSleepActivities() : mAtmService.isSleepingLocked();
     }
 
@@ -3827,7 +3824,7 @@
 
                 // Notify the pinned stack upon all windows drawn. If there was an animation in
                 // progress then this signal will resume that animation.
-                final ActivityStack pinnedStack = mDisplayContent.getPinnedStack();
+                final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask();
                 if (pinnedStack != null) {
                     pinnedStack.onAllWindowsDrawn();
                 }
@@ -4449,7 +4446,7 @@
             return visibleIgnoringKeyguard;
         }
 
-        final ActivityStack stack = getActivityStack();
+        final ActivityStack stack = getRootTask();
         if (stack == null) {
             return false;
         }
@@ -4471,7 +4468,7 @@
     }
 
     boolean shouldBeVisible() {
-        final ActivityStack stack = getActivityStack();
+        final ActivityStack stack = getRootTask();
         if (stack == null) {
             return false;
         }
@@ -4492,7 +4489,7 @@
         // If this activity is paused, tell it to now show its window.
         if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
                 "Making visible and scheduling visibility: " + this);
-        final ActivityStack stack = getActivityStack();
+        final ActivityStack stack = getRootTask();
         try {
             if (stack.mTranslucentActivityWaiting != null) {
                 updateOptionsLocked(returningOptions);
@@ -4578,7 +4575,7 @@
             if (DEBUG_VISIBILITY) {
                 Slog.v(TAG_VISIBILITY, "Resume visible activity, " + this);
             }
-            return getActivityStack().resumeTopActivityUncheckedLocked(activeActivity /* prev */,
+            return getRootTask().resumeTopActivityUncheckedLocked(activeActivity /* prev */,
                     null /* options */);
         } else if (shouldPauseActivity(activeActivity)) {
             if (DEBUG_VISIBILITY) {
@@ -4643,7 +4640,7 @@
      */
     private boolean shouldBeResumed(ActivityRecord activeActivity) {
         return shouldMakeActive(activeActivity) && isFocusable()
-                && getActivityStack().getVisibility(activeActivity) == STACK_VISIBILITY_VISIBLE
+                && getRootTask().getVisibility(activeActivity) == STACK_VISIBILITY_VISIBLE
                 && canResumeByCompat();
     }
 
@@ -4672,7 +4669,7 @@
         // ActivityStack#ensureActivitiesVisible will bring the activity to a proper
         // active state.
         if (!isState(STARTED, RESUMED, PAUSED, STOPPED, STOPPING)
-                || getActivityStack().mTranslucentActivityWaiting != null) {
+                || getRootTask().mTranslucentActivityWaiting != null) {
             return false;
         }
 
@@ -4769,7 +4766,7 @@
         mStackSupervisor.reportResumedActivityLocked(this);
 
         resumeKeyDispatchingLocked();
-        final ActivityStack stack = getActivityStack();
+        final ActivityStack stack = getRootTask();
         mStackSupervisor.mNoAnimActivities.clear();
 
         // Mark the point when the activity is resuming
@@ -4862,7 +4859,7 @@
 
     void stopIfPossible() {
         if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + this);
-        final ActivityStack stack = getActivityStack();
+        final ActivityStack stack = getRootTask();
         if (isNoHistory()) {
             if (!finishing) {
                 if (!stack.shouldSleepActivities()) {
@@ -4919,7 +4916,7 @@
 
     void activityStopped(Bundle newIcicle, PersistableBundle newPersistentState,
             CharSequence description) {
-        final ActivityStack stack = getActivityStack();
+        final ActivityStack stack = getRootTask();
         final boolean isStopping = mState == STOPPING;
         if (!isStopping && mState != RESTARTING_PROCESS) {
             Slog.i(TAG, "Activity reported stop, but no longer stopping: " + this);
@@ -4969,7 +4966,7 @@
             mStackSupervisor.mStoppingActivities.add(this);
         }
 
-        final ActivityStack stack = getActivityStack();
+        final ActivityStack stack = getRootTask();
         // If we already have a few activities waiting to stop, then give up on things going idle
         // and start clearing them out. Or if r is the last of activity of the last task the stack
         // will be empty and must be cleared immediately.
@@ -5005,7 +5002,7 @@
             return false;
         }
 
-        final ActivityStack stack = getActivityStack();
+        final ActivityStack stack = getRootTask();
         if (stack == null) {
             return false;
         }
@@ -5021,7 +5018,7 @@
 
     void finishLaunchTickingLocked() {
         launchTickTime = 0;
-        final ActivityStack stack = getActivityStack();
+        final ActivityStack stack = getRootTask();
         if (stack == null) {
             return;
         }
@@ -5491,13 +5488,13 @@
 
     static ActivityRecord isInStackLocked(IBinder token) {
         final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-        return (r != null) ? r.getActivityStack().isInStackLocked(r) : null;
+        return (r != null) ? r.getRootTask().isInStackLocked(r) : null;
     }
 
     static ActivityStack getStackLocked(IBinder token) {
         final ActivityRecord r = ActivityRecord.isInStackLocked(token);
         if (r != null) {
-            return r.getActivityStack();
+            return r.getRootTask();
         }
         return null;
     }
@@ -5507,7 +5504,7 @@
      *         {@link android.view.Display#INVALID_DISPLAY} if not attached.
      */
     int getDisplayId() {
-        final ActivityStack stack = getActivityStack();
+        final ActivityStack stack = getRootTask();
         if (stack == null) {
             return INVALID_DISPLAY;
         }
@@ -5519,7 +5516,7 @@
             // This would be redundant.
             return false;
         }
-        final ActivityStack stack = getActivityStack();
+        final ActivityStack stack = getRootTask();
         if (isState(RESUMED) || stack == null || this == stack.mPausingActivity || !mHaveState
                 || !stopped) {
             // We're not ready for this kind of thing.
@@ -6715,7 +6712,7 @@
     void savePinnedStackBounds() {
         // Leaving PiP to fullscreen, save the snap fraction based on the pre-animation bounds
         // for the next re-entry into PiP (assuming the activity is not hidden or destroyed)
-        final ActivityStack pinnedStack = mDisplayContent.getPinnedStack();
+        final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask();
         if (pinnedStack == null) return;
         final Rect stackBounds;
         if (pinnedStack.lastAnimatingBoundsWasToFullscreen()) {
@@ -6755,7 +6752,7 @@
     private void applyAspectRatio(Rect outBounds, Rect containingAppBounds,
             Rect containingBounds) {
         final float maxAspectRatio = info.maxAspectRatio;
-        final ActivityStack stack = getActivityStack();
+        final ActivityStack stack = getRootTask();
         final float minAspectRatio = info.minAspectRatio;
 
         if (task == null || stack == null || (inMultiWindowMode() && !shouldUseSizeCompatMode())
@@ -6861,7 +6858,7 @@
      */
     boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
             boolean ignoreVisibility) {
-        final ActivityStack stack = getActivityStack();
+        final ActivityStack stack = getRootTask();
         if (stack.mConfigWillChange) {
             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                     "Skipping config check (will change): " + this);
@@ -7406,7 +7403,7 @@
         if (!getTurnScreenOnFlag()) {
             return false;
         }
-        final ActivityStack stack = getActivityStack();
+        final ActivityStack stack = getRootTask();
         return stack != null &&
                 stack.checkKeyguardVisibility(this, true /* shouldBeVisible */, true /* isTop */);
     }
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 60e0f51e..942be84 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -277,9 +277,6 @@
 
     int mCurrentUser;
 
-    /** Unique identifier */
-    final int mStackId;
-
     /** For comparison with DisplayContent bounds. */
     private Rect mTmpRect = new Rect();
     private Rect mTmpRect2 = new Rect();
@@ -633,7 +630,6 @@
                 _realActivitySuspended, userSetupComplete, minWidth, minHeight, info, _voiceSession,
                 _voiceInteractor, stack);
 
-        mStackId = mTaskId;
         mDockedStackMinimizeThickness = mWmService.mContext.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.docked_stack_minimize_thickness);
         EventLogTags.writeWmStackCreated(id);
@@ -732,11 +728,11 @@
                 setBounds(newBounds);
                 newBounds.set(newBounds);
             } else if (overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
-                Rect dockedBounds = display.getSplitScreenPrimaryStack().getBounds();
+                Rect dockedBounds = display.getRootSplitScreenPrimaryTask().getBounds();
                 final boolean isMinimizedDock =
                         display.mDisplayContent.getDockedDividerController().isMinimizedDock();
                 if (isMinimizedDock) {
-                    Task topTask = display.getSplitScreenPrimaryStack().getTopMostTask();
+                    Task topTask = display.getRootSplitScreenPrimaryTask().getTopMostTask();
                     if (topTask != null) {
                         dockedBounds = topTask.getBounds();
                     }
@@ -830,7 +826,7 @@
         final int currentOverrideMode = getRequestedOverrideWindowingMode();
         final DisplayContent display = getDisplay();
         final Task topTask = getTopMostTask();
-        final ActivityStack splitScreenStack = display.getSplitScreenPrimaryStack();
+        final ActivityStack splitScreenStack = display.getRootSplitScreenPrimaryTask();
         int windowingMode = preferredWindowingMode;
         if (preferredWindowingMode == WINDOWING_MODE_UNDEFINED
                 && isTransientWindowingMode(currentMode)) {
@@ -851,7 +847,7 @@
             windowingMode = mRestoreOverrideWindowingMode;
         }
 
-        final boolean alreadyInSplitScreenMode = display.hasSplitScreenPrimaryStack();
+        final boolean alreadyInSplitScreenMode = display.hasSplitScreenPrimaryTask();
 
         // Don't send non-resizeable notifications if the windowing mode changed was a side effect
         // of us entering split-screen mode.
@@ -868,7 +864,7 @@
                 // warning toast about it.
                 mAtmService.getTaskChangeNotificationController()
                         .notifyActivityDismissingDockedStack();
-                final ActivityStack primarySplitStack = display.getSplitScreenPrimaryStack();
+                final ActivityStack primarySplitStack = display.getRootSplitScreenPrimaryTask();
                 primarySplitStack.setWindowingModeInSurfaceTransaction(WINDOWING_MODE_UNDEFINED,
                         false /* animate */, false /* showRecents */,
                         false /* enteringSplitScreenMode */, true /* deferEnsuringVisibility */,
@@ -1118,7 +1114,7 @@
             return null;
         }
         final Task task = r.getTask();
-        final ActivityStack stack = r.getActivityStack();
+        final ActivityStack stack = r.getRootTask();
         if (stack != null && task.mChildren.contains(r) && mChildren.contains(task)) {
             if (stack != this) Slog.w(TAG,
                     "Illegal state! task does not point to stack it is in.");
@@ -1165,7 +1161,7 @@
             final ActivityStack topFullScreenStack =
                     display.getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
             if (topFullScreenStack != null) {
-                final ActivityStack primarySplitScreenStack = display.getSplitScreenPrimaryStack();
+                final ActivityStack primarySplitScreenStack = display.getRootSplitScreenPrimaryTask();
                 if (display.getIndexOf(topFullScreenStack)
                         > display.getIndexOf(primarySplitScreenStack)) {
                     primarySplitScreenStack.moveToFront(reason + " splitScreenToTop");
@@ -1347,7 +1343,7 @@
 
     private boolean containsActivityFromStack(List<ActivityRecord> rs) {
         for (ActivityRecord r : rs) {
-            if (r.getActivityStack() == this) {
+            if (r.getRootTask() == this) {
                 return true;
             }
         }
@@ -1563,7 +1559,7 @@
         // focus). Also if there is an active pinned stack - we always want to notify it about
         // task stack changes, because its positioning may depend on it.
         if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause
-                || (getDisplay() != null && getDisplay().hasPinnedStack())) {
+                || (getDisplay() != null && getDisplay().hasPinnedTask())) {
             mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
             mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
         }
@@ -1873,7 +1869,7 @@
         final DisplayContent displayContent = getDisplay();
         if (displayContent == null) {
             throw new IllegalStateException("Stack is not attached to any display, stackId="
-                    + mStackId);
+                    + getRootTaskId());
         }
 
         final int flags = displayContent.mDisplay.getFlags();
@@ -2592,7 +2588,7 @@
             return false;
         }
         final ActivityStack targetStack = toFrontTask != null
-                ? toFrontTask.getStack() : toFrontActivity.getActivityStack();
+                ? toFrontTask.getStack() : toFrontActivity.getRootTask();
         if (targetStack != null && targetStack.isActivityTypeAssistant()) {
             // Ensure the task/activity being brought forward is not the assistant
             return false;
@@ -3073,7 +3069,7 @@
         }
 
         ActivityRecord topActivity = getDisplay().topRunningActivity();
-        ActivityStack topStack = topActivity.getActivityStack();
+        ActivityStack topStack = topActivity.getRootTask();
         if (topStack != null && topStack != this && topActivity.isState(RESUMED)) {
             // The new top activity is already resumed, so there's a good chance that nothing will
             // get resumed below. So, update visibility now in case the transition is closed
@@ -3102,7 +3098,7 @@
             return;
         }
 
-        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "stack.resize_" + mStackId);
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "stack.resize_" + getRootTaskId());
         mAtmService.deferWindowLayout();
         try {
             // Update override configurations of all tasks in the stack.
@@ -3219,7 +3215,7 @@
 
     boolean dump(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient,
             String dumpPackage, boolean needSep) {
-        pw.println("  Stack #" + mStackId
+        pw.println("  Stack #" + getRootTaskId()
                 + ": type=" + activityTypeToString(getActivityType())
                 + " mode=" + windowingModeToString(getWindowingMode()));
         pw.println("  isSleeping=" + shouldSleepActivities());
@@ -4484,7 +4480,7 @@
 
     @Override
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
-        pw.println(prefix + "mStackId=" + mStackId);
+        pw.println(prefix + "mStackId=" + getRootTaskId());
         pw.println(prefix + "mDeferRemoval=" + mDeferRemoval);
         pw.println(prefix + "mBounds=" + getRawBounds().toShortString());
         if (mMinimizeAmount != 0f) {
@@ -4515,14 +4511,6 @@
         mAnimatingActivityRegistry.dump(pw, "AnimatingApps:", prefix);
     }
 
-    String getName() {
-        return toShortString();
-    }
-
-    public String toShortString() {
-        return "Stack=" + mStackId;
-    }
-
     /**
      * For docked workspace (or workspace that's side-by-side to the docked), provides
      * information which side of the screen was the dock anchored.
@@ -4731,7 +4719,7 @@
             // activity early for the recents animation prior to the PiP animation starting, there
             // is no subsequent all-drawn signal. In this case, we can skip the pause when the home
             // stack is already visible and drawn.
-            final ActivityStack homeStack = mDisplayContent.getHomeStack();
+            final ActivityStack homeStack = mDisplayContent.getRootHomeTask();
             if (homeStack == null) {
                 return true;
             }
@@ -4863,7 +4851,7 @@
             @WindowTraceLogLevel int logLevel) {
         final long token = proto.start(fieldId);
         dumpDebugInnerStackOnly(proto, STACK, logLevel);
-        proto.write(com.android.server.am.ActivityStackProto.ID, mStackId);
+        proto.write(com.android.server.am.ActivityStackProto.ID, getRootTaskId());
 
         forAllTasks((t) -> {
             t.dumpDebugInner(proto, com.android.server.am.ActivityStackProto.TASKS, logLevel);
@@ -4891,7 +4879,7 @@
 
         final long token = proto.start(fieldId);
         super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
-        proto.write(StackProto.ID, mStackId);
+        proto.write(StackProto.ID, getRootTaskId());
         forAllTasks((t) -> {
             t.dumpDebugInnerTaskOnly(proto, StackProto.TASKS, logLevel);
         }, true /* traverseTopToBottom */, this);
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index f2ce7e8..2c0f3e6 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -824,7 +824,7 @@
                         false /* markFrozenIfConfigChanged */, true /* deferResume */);
             }
 
-            if (r.getActivityStack().checkKeyguardVisibility(r, true /* shouldBeVisible */,
+            if (r.getRootTask().checkKeyguardVisibility(r, true /* shouldBeVisible */,
                     true /* isTop */) && r.allowMoveToFront()) {
                 // We only set the visibility to true if the activity is not being launched in
                 // background, and is allowed to be visible based on keyguard state. This avoids
@@ -1582,7 +1582,7 @@
         }
 
         final ActivityStack stack =
-                mRootWindowContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
+                mRootWindowContainer.getDefaultDisplay().getRootSplitScreenPrimaryTask();
         if (stack == null) {
             Slog.w(TAG, "resizeDockedStackLocked: docked stack not found");
             return;
@@ -1670,7 +1670,7 @@
     void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
         // TODO(multi-display): The display containing the stack should be passed in.
         final ActivityStack stack =
-                mRootWindowContainer.getDefaultDisplay().getPinnedStack();
+                mRootWindowContainer.getDefaultDisplay().getRootPinnedTask();
         if (stack == null) {
             Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
             return;
@@ -1891,13 +1891,13 @@
     // TODO: Look into changing users to this method to DisplayContent.resolveWindowingMode()
     ActivityStack getReparentTargetStack(Task task, ActivityStack stack, boolean toTop) {
         final ActivityStack prevStack = task.getStack();
-        final int stackId = stack.mStackId;
+        final int rootTaskId = stack.mTaskId;
         final boolean inMultiWindowMode = stack.inMultiWindowMode();
 
         // Check that we aren't reparenting to the same stack that the task is already in
-        if (prevStack != null && prevStack.mStackId == stackId) {
+        if (prevStack != null && prevStack.mTaskId == rootTaskId) {
             Slog.w(TAG, "Can not reparent to same stack, task=" + task
-                    + " already in stackId=" + stackId);
+                    + " already in stackId=" + rootTaskId);
             return prevStack;
         }
 
@@ -1912,7 +1912,7 @@
         // multi-display.
         if (stack.getDisplayId() != DEFAULT_DISPLAY && !mService.mSupportsMultiDisplay) {
             throw new IllegalArgumentException("Device doesn't support multi-display, can not"
-                    + " reparent task=" + task + " to stackId=" + stackId);
+                    + " reparent task=" + task + " to stackId=" + rootTaskId);
         }
 
         // Ensure that we aren't trying to move into a freeform stack without freeform support
@@ -1993,7 +1993,7 @@
 
     void activitySleptLocked(ActivityRecord r) {
         mGoingToSleepActivities.remove(r);
-        final ActivityStack s = r.getActivityStack();
+        final ActivityStack s = r.getRootTask();
         if (s != null) {
             s.checkReadyForSleep();
         } else {
@@ -2029,7 +2029,7 @@
         // A resumed activity cannot be stopping. remove from list
         mStoppingActivities.remove(r);
 
-        final ActivityStack stack = r.getActivityStack();
+        final ActivityStack stack = r.getRootTask();
         if (stack.getDisplay().allResumedActivitiesComplete()) {
             mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
             // Make sure activity & window visibility should be identical
@@ -2082,7 +2082,7 @@
             if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + s.nowVisible
                     + " animating=" + animating + " finishing=" + s.finishing);
 
-            final ActivityStack stack = s.getActivityStack();
+            final ActivityStack stack = s.getRootTask();
             final boolean shouldSleepOrShutDown = stack != null
                     ? stack.shouldSleepOrShutDownActivities()
                     : mService.isSleepingOrShuttingDownLocked();
@@ -2395,7 +2395,7 @@
         final boolean isSecondaryDisplayPreferred =
                 (preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY);
         final boolean inSplitScreenMode = actualStack != null
-                && actualStack.getDisplay().hasSplitScreenPrimaryStack();
+                && actualStack.getDisplay().hasSplitScreenPrimaryTask();
         if (((!inSplitScreenMode && preferredWindowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
                 && !isSecondaryDisplayPreferred) || !task.isActivityTypeStandardOrUndefined()) {
             return;
@@ -2444,7 +2444,7 @@
             // we need to move it to top of fullscreen stack, otherwise it will be covered.
 
             final ActivityStack dockedStack =
-                    task.getStack().getDisplay().getSplitScreenPrimaryStack();
+                    task.getStack().getDisplay().getRootSplitScreenPrimaryTask();
             if (dockedStack != null) {
                 // Display a warning toast that we tried to put an app that doesn't support
                 // split-screen in split-screen.
@@ -2473,7 +2473,7 @@
         final ActivityRecord r = ActivityRecord.isInStackLocked(token);
         if (r != null) {
             r.finishRelaunching();
-            if (r.getActivityStack().shouldSleepOrShutDownActivities()) {
+            if (r.getRootTask().shouldSleepOrShutDownActivities()) {
                 r.setSleeping(true, true);
             }
         }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 6587226..26812f4 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -937,7 +937,7 @@
         }
 
         final ActivityStack resultStack = resultRecord == null
-                ? null : resultRecord.getActivityStack();
+                ? null : resultRecord.getRootTask();
 
         if (err != START_SUCCESS) {
             if (resultRecord != null) {
@@ -1365,7 +1365,7 @@
                     break;
                 case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
                     final ActivityStack homeStack =
-                            startedActivityStack.getDisplay().getHomeStack();
+                            startedActivityStack.getDisplay().getRootHomeTask();
                     if (homeStack != null && homeStack.shouldBeVisible(null /* starting */)) {
                         mService.mWindowManager.showRecentApps();
                     }
@@ -1425,7 +1425,7 @@
      * @return the stack where the successful started activity resides.
      */
     private @Nullable ActivityStack handleStartResult(@NonNull ActivityRecord started, int result) {
-        final ActivityStack currentStack = started.getActivityStack();
+        final ActivityStack currentStack = started.getRootTask();
         ActivityStack startedActivityStack = currentStack != null ? currentStack : mTargetStack;
 
         if (ActivityManager.isStartResultSuccessful(result)) {
@@ -1446,7 +1446,7 @@
         // If we are not able to proceed, disassociate the activity from the task. Leaving an
         // activity in an incomplete state can lead to issues, such as performing operations
         // without a window container.
-        final ActivityStack stack = mStartActivity.getActivityStack();
+        final ActivityStack stack = mStartActivity.getRootTask();
         if (stack != null) {
             mStartActivity.finishIfPossible("startActivity", true /* oomAdj */);
         }
@@ -2231,7 +2231,7 @@
             return;
         }
         if (!mSourceRecord.finishing) {
-            mSourceStack = mSourceRecord.getActivityStack();
+            mSourceStack = mSourceRecord.getRootTask();
             return;
         }
 
@@ -2314,7 +2314,7 @@
      * @return {@link ActivityRecord} brought to front.
      */
     private void setTargetStackIfNeeded(ActivityRecord intentActivity) {
-        mTargetStack = intentActivity.getActivityStack();
+        mTargetStack = intentActivity.getRootTask();
         mTargetStack.mLastPausedActivity = null;
         // If the target task is not in the front, then we need to bring it to the front...
         // except...  well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
@@ -2371,7 +2371,7 @@
         }
         // Need to update mTargetStack because if task was moved out of it, the original stack may
         // be destroyed.
-        mTargetStack = intentActivity.getActivityStack();
+        mTargetStack = intentActivity.getRootTask();
         mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(),
                 WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY, mTargetStack);
     }
@@ -2422,10 +2422,11 @@
             return;
         }
 
-        final ActivityStack stack = task.getStack();
-        if (stack != null && stack.inPinnedWindowingMode()) {
-            mService.animateResizePinnedStack(stack.mStackId, bounds, -1);
+        final Task rootTask = task.getRootTask();
+        if (rootTask != null && rootTask.inPinnedWindowingMode()) {
+            mService.animateResizePinnedStack(rootTask.mTaskId, bounds, -1);
         } else {
+            // TODO: I don't believe it is possible to reach this else condition anymore...
             task.setBounds(bounds);
         }
     }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 8491bc2..31b7c68 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1612,7 +1612,7 @@
             if (mController != null) {
                 // Find the first activity that is not finishing.
                 final ActivityRecord next =
-                        r.getActivityStack().topRunningActivity(token, INVALID_TASK_ID);
+                        r.getRootTask().topRunningActivity(token, INVALID_TASK_ID);
                 if (next != null) {
                     // ask watcher if this is allowed
                     boolean resumeOK = true;
@@ -2037,7 +2037,7 @@
         synchronized (mGlobalLock) {
             ActivityRecord r = mRootWindowContainer.isInAnyStack(token);
             if (r != null) {
-                r.getActivityStack().notifyActivityDrawnLocked(r);
+                r.getRootTask().notifyActivityDrawnLocked(r);
             }
         }
     }
@@ -2073,7 +2073,7 @@
             synchronized (mGlobalLock) {
                 ActivityStack focusedStack = getTopDisplayFocusedStack();
                 if (focusedStack != null) {
-                    return mRootWindowContainer.getStackInfo(focusedStack.mStackId);
+                    return mRootWindowContainer.getStackInfo(focusedStack.mTaskId);
                 }
                 return null;
             }
@@ -2175,7 +2175,7 @@
         synchronized (mGlobalLock) {
             final ActivityRecord srec = ActivityRecord.forTokenLocked(token);
             if (srec != null) {
-                return srec.getActivityStack().shouldUpRecreateTaskLocked(srec, destAffinity);
+                return srec.getRootTask().shouldUpRecreateTaskLocked(srec, destAffinity);
             }
         }
         return false;
@@ -2188,7 +2188,7 @@
         synchronized (mGlobalLock) {
             final ActivityRecord r = ActivityRecord.forTokenLocked(token);
             if (r != null) {
-                return r.getActivityStack().navigateUpTo(
+                return r.getRootTask().navigateUpTo(
                         r, destIntent, resultCode, resultData);
             }
             return false;
@@ -2354,7 +2354,7 @@
             if (r == null) {
                 return;
             }
-            ActivityStack stack = r.getActivityStack();
+            ActivityStack stack = r.getRootTask();
             if (stack != null && stack.isSingleTaskInstance()) {
                 // Single-task stacks are used for activities which are presented in floating
                 // windows above full screen activities. Instead of directly finishing the
@@ -2582,7 +2582,7 @@
                         r, resultWho, requestCode);
                 // TODO: This should probably only loop over the task since you need to be in the
                 // same task to return results.
-                r.getActivityStack().forAllActivities(c);
+                r.getRootTask().forAllActivities(c);
                 c.recycle();
 
                 updateOomAdj();
@@ -3215,7 +3215,7 @@
                                     + ainfo.applicationInfo.uid + ", calling uid=" + callingUid);
                 }
 
-                final ActivityStack stack = r.getActivityStack();
+                final ActivityStack stack = r.getRootTask();
                 final Task task = stack.createTask(
                         mStackSupervisor.getNextTaskIdForUser(r.mUserId), ainfo, intent, !ON_TOP);
                 if (!mRecentTasks.addToBottom(task)) {
@@ -3551,7 +3551,7 @@
                                     + token);
                 }
 
-                final ActivityStack stack = r.getActivityStack();
+                final ActivityStack stack = r.getRootTask();
                 if (stack == null) {
                     throw new IllegalStateException("toggleFreeformWindowingMode: the activity "
                             + "doesn't have a stack");
@@ -4080,7 +4080,7 @@
         try {
             synchronized (mGlobalLock) {
                 final ActivityStack stack =
-                        mRootWindowContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
+                        mRootWindowContainer.getDefaultDisplay().getRootSplitScreenPrimaryTask();
                 if (stack == null) {
                     Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found.");
                     return;
@@ -4121,7 +4121,7 @@
         try {
             synchronized (mGlobalLock) {
                 final ActivityStack stack =
-                        mRootWindowContainer.getDefaultDisplay().getPinnedStack();
+                        mRootWindowContainer.getDefaultDisplay().getRootPinnedTask();
                 if (stack == null) {
                     Slog.w(TAG, "dismissPip: pinned stack not found.");
                     return;
@@ -4235,14 +4235,14 @@
     }
 
     private boolean isInPictureInPictureMode(ActivityRecord r) {
-        if (r == null || r.getActivityStack() == null || !r.inPinnedWindowingMode()
-                || r.getActivityStack().isInStackLocked(r) == null) {
+        if (r == null || r.getRootTask() == null || !r.inPinnedWindowingMode()
+                || r.getRootTask().isInStackLocked(r) == null) {
             return false;
         }
 
         // If we are animating to fullscreen then we have already dispatched the PIP mode
         // changed, so we should reflect that check here as well.
-        final ActivityStack taskStack = r.getActivityStack();
+        final ActivityStack taskStack = r.getRootTask();
         return !taskStack.isAnimatingBoundsToFullscreen();
     }
 
@@ -4281,7 +4281,7 @@
                                 r.pictureInPictureArgs.getSourceRectHint());
                         mRootWindowContainer.moveActivityToPinnedStack(
                                 r, sourceBounds, aspectRatio, "enterPictureInPictureMode");
-                        final ActivityStack stack = r.getActivityStack();
+                        final ActivityStack stack = r.getRootTask();
                         stack.setPictureInPictureAspectRatio(aspectRatio);
                         stack.setPictureInPictureActions(actions);
                         MetricsLoggerWrapper.logPictureInPictureEnter(mContext,
@@ -4326,7 +4326,7 @@
                     // If the activity is already in picture-in-picture, update the pinned stack now
                     // if it is not already expanding to fullscreen. Otherwise, the arguments will
                     // be used the next time the activity enters PiP
-                    final ActivityStack stack = r.getActivityStack();
+                    final ActivityStack stack = r.getRootTask();
                     if (!stack.isAnimatingBoundsToFullscreen()) {
                         stack.setPictureInPictureAspectRatio(
                                 r.pictureInPictureArgs.getAspectRatio());
@@ -4923,7 +4923,7 @@
             Slog.i(TAG, "Moving " + r.shortComponentName + " from display " + r.getDisplayId()
                     + " to main display for VR");
             mRootWindowContainer.moveStackToDisplay(
-                    r.getStackId(), DEFAULT_DISPLAY, true /* toTop */);
+                    r.getRootTaskId(), DEFAULT_DISPLAY, true /* toTop */);
         }
         mH.post(() -> {
             if (!mVrController.onVrModeChanged(r)) {
@@ -6752,7 +6752,7 @@
                 int requestCode, int resultCode, Intent data) {
             synchronized (mGlobalLock) {
                 final ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
-                if (r != null && r.getActivityStack() != null) {
+                if (r != null && r.getRootTask() != null) {
                     r.sendResult(callingUid, resultWho, requestCode, resultCode, data);
                 }
             }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index dbc9911..825f93c 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -19,6 +19,7 @@
 import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
@@ -294,7 +295,7 @@
 
     /** The containers below are the only child containers {@link #mWindowContainers} can have. */
     // Contains all window containers that are related to apps (Activities)
-    private final TaskStackContainers mTaskStackContainers = new TaskStackContainers(mWmService);
+    private final TaskContainers mTaskContainers = new TaskContainers(mWmService);
     // Contains all non-app window containers that should be displayed above the app containers
     // (e.g. Status bar)
     private final AboveAppWindowContainers mAboveAppWindowsContainers =
@@ -1933,39 +1934,25 @@
         return (mDisplay.getFlags() & FLAG_PRIVATE) != 0;
     }
 
-    ActivityStack getHomeStack() {
-        return mTaskStackContainers.getHomeStack();
+    ActivityStack getRootHomeTask() {
+        return mTaskContainers.getRootHomeTask();
     }
 
-    ActivityStack getRecentsStack() {
-        return mTaskStackContainers.getRecentsStack();
+    /** @return The primary split-screen task, and {@code null} otherwise. */
+    ActivityStack getRootSplitScreenPrimaryTask() {
+        return mTaskContainers.getRootSplitScreenPrimaryTask();
     }
 
-    /**
-     * @return The primary split-screen stack, and {@code null} otherwise.
-     */
-    ActivityStack getSplitScreenPrimaryStack() {
-        return mTaskStackContainers.getSplitScreenPrimaryStack();
+    boolean hasSplitScreenPrimaryTask() {
+        return getRootSplitScreenPrimaryTask() != null;
     }
 
-    boolean hasSplitScreenPrimaryStack() {
-        return getSplitScreenPrimaryStack() != null;
+    ActivityStack getRootPinnedTask() {
+        return mTaskContainers.getRootPinnedTask();
     }
 
-    /**
-     * Like {@link #getSplitScreenPrimaryStack}, but also returns the stack if it's currently
-     * not visible.
-     */
-    ActivityStack getSplitScreenPrimaryStackIgnoringVisibility() {
-        return mTaskStackContainers.getSplitScreenPrimaryStack();
-    }
-
-    ActivityStack getPinnedStack() {
-        return mTaskStackContainers.getPinnedStack();
-    }
-
-    boolean hasPinnedStack() {
-        return mTaskStackContainers.getPinnedStack() != null;
+    boolean hasPinnedTask() {
+        return mTaskContainers.getRootPinnedTask() != null;
     }
 
     /**
@@ -1981,45 +1968,45 @@
      * activity type. Null is no compatible stack on the display.
      */
     ActivityStack getStack(int windowingMode, int activityType) {
-        return mTaskStackContainers.getStack(windowingMode, activityType);
+        return mTaskContainers.getStack(windowingMode, activityType);
     }
 
     protected int getStackCount() {
-        return mTaskStackContainers.mChildren.size();
+        return mTaskContainers.mChildren.size();
     }
 
     protected ActivityStack getStackAt(int index) {
-        return mTaskStackContainers.mChildren.get(index);
+        return mTaskContainers.mChildren.get(index);
     }
 
     int getIndexOf(ActivityStack stack) {
-        return mTaskStackContainers.getIndexOf(stack);
+        return mTaskContainers.getIndexOf(stack);
     }
 
     void removeStack(ActivityStack stack) {
-        mTaskStackContainers.removeChild(stack);
+        mTaskContainers.removeChild(stack);
     }
 
     @VisibleForTesting
     WindowList<ActivityStack> getStacks() {
-        return mTaskStackContainers.mChildren;
+        return mTaskContainers.mChildren;
     }
 
     @VisibleForTesting
     ActivityStack getTopStack() {
-        return mTaskStackContainers.getTopStack();
+        return mTaskContainers.getTopStack();
     }
 
     ArrayList<Task> getVisibleTasks() {
-        return mTaskStackContainers.getVisibleTasks();
+        return mTaskContainers.getVisibleTasks();
     }
 
     SurfaceControl getSplitScreenDividerAnchor() {
-        return mTaskStackContainers.getSplitScreenDividerAnchor();
+        return mTaskContainers.getSplitScreenDividerAnchor();
     }
 
     void onStackWindowingModeChanged(ActivityStack stack) {
-        mTaskStackContainers.onStackWindowingModeChanged(stack);
+        mTaskContainers.onStackWindowingModeChanged(stack);
     }
 
     /**
@@ -2052,7 +2039,7 @@
 
         // If there was no pinned stack, we still need to notify the controller of the display info
         // update as a result of the config change.
-        if (mPinnedStackControllerLocked != null && !hasPinnedStack()) {
+        if (mPinnedStackControllerLocked != null && !hasPinnedTask()) {
             mPinnedStackControllerLocked.onDisplayInfoChanged(getDisplayInfo());
         }
     }
@@ -2139,7 +2126,7 @@
         }
 
         // Top system windows are not requesting an orientation. Start searching from apps.
-        return mTaskStackContainers.getOrientation();
+        return mTaskContainers.getOrientation();
     }
 
     void updateDisplayInfo() {
@@ -2323,11 +2310,11 @@
 
     void setStackOnDisplay(ActivityStack stack, int position) {
         if (DEBUG_STACK) Slog.d(TAG_WM, "Set stack=" + stack + " on displayId=" + mDisplayId);
-        mTaskStackContainers.addChild(stack, position);
+        mTaskContainers.addChild(stack, position);
     }
 
     void moveStackToDisplay(ActivityStack stack, boolean onTop) {
-        stack.reparent(mTaskStackContainers, onTop ? POSITION_TOP: POSITION_BOTTOM);
+        stack.reparent(mTaskContainers, onTop ? POSITION_TOP: POSITION_BOTTOM);
     }
 
     @Override
@@ -2364,7 +2351,7 @@
     }
 
     void positionStackAt(int position, ActivityStack child, boolean includingParents) {
-        mTaskStackContainers.positionChildAt(position, child, includingParents);
+        mTaskContainers.positionChildAt(position, child, includingParents);
         layoutAndAssignWindowLayersIfNeeded();
     }
 
@@ -2395,7 +2382,7 @@
      */
     Task findTaskForResizePoint(int x, int y) {
         final int delta = dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
-        return mTmpTaskForResizePointSearchResult.process(mTaskStackContainers, x, y, delta);
+        return mTmpTaskForResizePointSearchResult.process(mTaskContainers, x, y, delta);
     }
 
     void updateTouchExcludeRegion() {
@@ -2411,7 +2398,7 @@
             final PooledConsumer c = PooledLambda.obtainConsumer(
                     DisplayContent::processTaskForTouchExcludeRegion, this,
                     PooledLambda.__(Task.class), focusedTask, delta);
-            mTaskStackContainers.forAllTasks(c);
+            mTaskContainers.forAllTasks(c);
             c.recycle();
 
             // If we removed the focused task above, add it back and only leave its
@@ -2435,7 +2422,7 @@
         }
         amendWindowTapExcludeRegion(mTouchExcludeRegion);
         // TODO(multi-display): Support docked stacks on secondary displays.
-        if (mDisplayId == DEFAULT_DISPLAY && getSplitScreenPrimaryStack() != null) {
+        if (mDisplayId == DEFAULT_DISPLAY && getRootSplitScreenPrimaryTask() != null) {
             mDividerControllerLocked.getTouchRegion(mTmpRect);
             mTmpRegion.set(mTmpRect);
             mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
@@ -2581,8 +2568,8 @@
             float dividerAnimationTarget) {
         boolean updated = false;
 
-        for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
-            final ActivityStack stack = mTaskStackContainers.getChildAt(i);
+        for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
+            final ActivityStack stack = mTaskContainers.getChildAt(i);
             if (stack == null || !stack.isAdjustedForIme()) {
                 continue;
             }
@@ -2610,8 +2597,8 @@
 
     boolean clearImeAdjustAnimation() {
         boolean changed = false;
-        for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
-            final ActivityStack stack = mTaskStackContainers.getChildAt(i);
+        for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
+            final ActivityStack stack = mTaskContainers.getChildAt(i);
             if (stack != null && stack.isAdjustedForIme()) {
                 stack.resetAdjustedForIme(true /* adjustBoundsNow */);
                 changed  = true;
@@ -2621,8 +2608,8 @@
     }
 
     void beginImeAdjustAnimation() {
-        for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
-            final ActivityStack stack = mTaskStackContainers.getChildAt(i);
+        for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
+            final ActivityStack stack = mTaskContainers.getChildAt(i);
             if (stack.isVisible() && stack.isAdjustedForIme()) {
                 stack.beginImeAdjustAnimation();
             }
@@ -2633,7 +2620,7 @@
         final WindowState imeWin = mInputMethodWindow;
         final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
                 && !mDividerControllerLocked.isImeHideRequested();
-        final ActivityStack dockedStack = getSplitScreenPrimaryStack();
+        final ActivityStack dockedStack = getRootSplitScreenPrimaryTask();
         final boolean dockVisible = dockedStack != null;
         final Task topDockedTask = dockVisible ? dockedStack.getTask((t) -> true): null;
         final ActivityStack imeTargetStack = mWmService.getImeFocusStackLocked();
@@ -2659,8 +2646,8 @@
         // - If IME is not visible, divider is not moved and is normal width.
 
         if (imeVisible && dockVisible && (imeOnTop || imeOnBottom) && !dockMinimized) {
-            for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
-                final ActivityStack stack = mTaskStackContainers.getChildAt(i);
+            for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
+                final ActivityStack stack = mTaskContainers.getChildAt(i);
                 final boolean isDockedOnBottom = stack.getDockSide() == DOCKED_BOTTOM;
                 if (stack.isVisible() && (imeOnBottom || isDockedOnBottom)
                         && stack.inSplitScreenWindowingMode()) {
@@ -2672,8 +2659,8 @@
             mDividerControllerLocked.setAdjustedForIme(
                     imeOnBottom /*ime*/, true /*divider*/, true /*animate*/, imeWin, imeHeight);
         } else {
-            for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
-                final ActivityStack stack = mTaskStackContainers.getChildAt(i);
+            for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
+                final ActivityStack stack = mTaskContainers.getChildAt(i);
                 stack.resetAdjustedForIme(!dockVisible);
             }
             mDividerControllerLocked.setAdjustedForIme(
@@ -2683,8 +2670,8 @@
     }
 
     void prepareFreezingTaskBounds() {
-        for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mTaskStackContainers.getChildAt(stackNdx);
+        for (int stackNdx = mTaskContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityStack stack = mTaskContainers.getChildAt(stackNdx);
             stack.prepareFreezingTaskBounds();
         }
     }
@@ -2760,7 +2747,7 @@
         proto.write(SINGLE_TASK_INSTANCE, mSingleTaskInstance);
         final ActivityStack focusedStack = getFocusedStack();
         if (focusedStack != null) {
-            proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
+            proto.write(FOCUSED_STACK_ID, focusedStack.getRootTaskId());
             final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
             if (focusedActivity != null) {
                 focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
@@ -2786,8 +2773,8 @@
         final long token = proto.start(fieldId);
         super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
         proto.write(ID, mDisplayId);
-        for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mTaskStackContainers.getChildAt(stackNdx);
+        for (int stackNdx = mTaskContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityStack stack = mTaskContainers.getChildAt(stackNdx);
             stack.dumpDebugInnerStackOnly(proto, STACKS, logLevel);
         }
         mDividerControllerLocked.dumpDebug(proto, DOCKED_STACK_DIVIDER_CONTROLLER);
@@ -2913,8 +2900,8 @@
 
         pw.println();
         pw.println(prefix + "Application tokens in top down Z order:");
-        for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = mTaskStackContainers.getChildAt(stackNdx);
+        for (int stackNdx = mTaskContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityStack stack = mTaskContainers.getChildAt(stackNdx);
             stack.dump(pw, prefix + "  ", dumpAll);
         }
 
@@ -2944,19 +2931,20 @@
         pw.println();
 
         // Dump stack references
-        final ActivityStack homeStack = getHomeStack();
+        final ActivityStack homeStack = getRootHomeTask();
         if (homeStack != null) {
             pw.println(prefix + "homeStack=" + homeStack.getName());
         }
-        final ActivityStack pinnedStack = getPinnedStack();
+        final ActivityStack pinnedStack = getRootPinnedTask();
         if (pinnedStack != null) {
             pw.println(prefix + "pinnedStack=" + pinnedStack.getName());
         }
-        final ActivityStack splitScreenPrimaryStack = getSplitScreenPrimaryStack();
+        final ActivityStack splitScreenPrimaryStack = getRootSplitScreenPrimaryTask();
         if (splitScreenPrimaryStack != null) {
             pw.println(prefix + "splitScreenPrimaryStack=" + splitScreenPrimaryStack.getName());
         }
-        final ActivityStack recentsStack = getRecentsStack();
+        final ActivityStack recentsStack =
+                getStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
         if (recentsStack != null) {
             pw.println(prefix + "recentsStack=" + recentsStack.getName());
         }
@@ -3994,7 +3982,7 @@
         }
 
         // Initialize state of exiting applications.
-        mTaskStackContainers.setExitingTokensHasVisible(hasVisible);
+        mTaskContainers.setExitingTokensHasVisible(hasVisible);
     }
 
     void removeExistingTokensIfPossible() {
@@ -4006,7 +3994,7 @@
         }
 
         // Time to remove any exiting applications?
-        mTaskStackContainers.removeExistingAppTokensIfPossible();
+        mTaskContainers.removeExistingAppTokensIfPossible();
     }
 
     @Override
@@ -4137,7 +4125,7 @@
      * Window container class that contains all containers on this display relating to Apps.
      * I.e Activities.
      */
-    private final class TaskStackContainers extends DisplayChildWindowContainer<ActivityStack> {
+    private final class TaskContainers extends DisplayChildWindowContainer<ActivityStack> {
         /**
          * A control placed at the appropriate level for transitions to occur.
          */
@@ -4160,14 +4148,13 @@
          */
         SurfaceControl mSplitScreenDividerAnchor = null;
 
-        // Cached reference to some special stacks we tend to get a lot so we don't need to loop
+        // Cached reference to some special tasks we tend to get a lot so we don't need to loop
         // through the list to find them.
-        private ActivityStack mHomeStack = null;
-        private ActivityStack mRecentsStack = null;
-        private ActivityStack mPinnedStack = null;
-        private ActivityStack mSplitScreenPrimaryStack = null;
+        private ActivityStack mRootHomeTask = null;
+        private ActivityStack mRootPinnedTask = null;
+        private ActivityStack mRootSplitScreenPrimaryTask = null;
 
-        TaskStackContainers(WindowManagerService service) {
+        TaskContainers(WindowManagerService service) {
             super(service);
         }
 
@@ -4177,15 +4164,15 @@
          */
         ActivityStack getStack(int windowingMode, int activityType) {
             if (activityType == ACTIVITY_TYPE_HOME) {
-                return mHomeStack;
+                return mRootHomeTask;
             }
             if (windowingMode == WINDOWING_MODE_PINNED) {
-                return mPinnedStack;
+                return mRootPinnedTask;
             } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                return mSplitScreenPrimaryStack;
+                return mRootSplitScreenPrimaryTask;
             }
-            for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
-                final ActivityStack stack = mTaskStackContainers.getChildAt(i);
+            for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
+                final ActivityStack stack = mTaskContainers.getChildAt(i);
                 if (activityType == ACTIVITY_TYPE_UNDEFINED
                         && windowingMode == stack.getWindowingMode()) {
                     // Passing in undefined type means we want to match the topmost stack with the
@@ -4201,31 +4188,27 @@
 
         @VisibleForTesting
         ActivityStack getTopStack() {
-            return mTaskStackContainers.getChildCount() > 0
-                    ? mTaskStackContainers.getChildAt(mTaskStackContainers.getChildCount() - 1) : null;
+            return mTaskContainers.getChildCount() > 0
+                    ? mTaskContainers.getChildAt(mTaskContainers.getChildCount() - 1) : null;
         }
 
         int getIndexOf(ActivityStack stack) {
-            return mTaskStackContainers.mChildren.indexOf(stack);
+            return mTaskContainers.mChildren.indexOf(stack);
         }
 
-        ActivityStack getHomeStack() {
-            if (mHomeStack == null && mDisplayId == DEFAULT_DISPLAY) {
+        ActivityStack getRootHomeTask() {
+            if (mRootHomeTask == null && mDisplayId == DEFAULT_DISPLAY) {
                 Slog.e(TAG_WM, "getHomeStack: Returning null from this=" + this);
             }
-            return mHomeStack;
+            return mRootHomeTask;
         }
 
-        ActivityStack getRecentsStack() {
-            return mRecentsStack;
+        ActivityStack getRootPinnedTask() {
+            return mRootPinnedTask;
         }
 
-        ActivityStack getPinnedStack() {
-            return mPinnedStack;
-        }
-
-        ActivityStack getSplitScreenPrimaryStack() {
-            return mSplitScreenPrimaryStack;
+        ActivityStack getRootSplitScreenPrimaryTask() {
+            return mRootSplitScreenPrimaryTask;
         }
 
         ArrayList<Task> getVisibleTasks() {
@@ -4241,7 +4224,7 @@
         void onStackWindowingModeChanged(ActivityStack stack) {
             removeStackReferenceIfNeeded(stack);
             addStackReferenceIfNeeded(stack);
-            if (stack == mPinnedStack && getTopStack() != stack) {
+            if (stack == mRootPinnedTask && getTopStack() != stack) {
                 // Looks like this stack changed windowing mode to pinned. Move it to the top.
                 positionChildAt(POSITION_TOP, stack, false /* includingParents */);
             }
@@ -4249,46 +4232,36 @@
 
         private void addStackReferenceIfNeeded(ActivityStack stack) {
             if (stack.isActivityTypeHome()) {
-                if (mHomeStack != null) {
-                    if (!stack.isDescendantOf(mHomeStack)) {
+                if (mRootHomeTask != null) {
+                    if (!stack.isDescendantOf(mRootHomeTask)) {
                         throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
-                                + mHomeStack + " already exist on display=" + this
+                                + mRootHomeTask + " already exist on display=" + this
                                 + " stack=" + stack);
                     }
                 } else {
-                    mHomeStack = stack;
-                }
-            } else if (stack.isActivityTypeRecents()) {
-                if (mRecentsStack != null && mRecentsStack != stack) {
-                    if (!stack.isDescendantOf(mRecentsStack)) {
-                        throw new IllegalArgumentException(
-                                "addStackReferenceIfNeeded: recents stack=" + mRecentsStack
-                                        + " already exist on display=" + this + " stack=" + stack);
-                    }
-                } else {
-                    mRecentsStack = stack;
+                    mRootHomeTask = stack;
                 }
             }
             final int windowingMode = stack.getWindowingMode();
             if (windowingMode == WINDOWING_MODE_PINNED) {
-                if (mPinnedStack != null) {
-                    if (!stack.isDescendantOf(mPinnedStack)) {
+                if (mRootPinnedTask != null) {
+                    if (!stack.isDescendantOf(mRootPinnedTask)) {
                         throw new IllegalArgumentException(
-                                "addStackReferenceIfNeeded: pinned stack=" + mPinnedStack
+                                "addStackReferenceIfNeeded: pinned stack=" + mRootPinnedTask
                                         + " already exist on display=" + this + " stack=" + stack);
                     }
                 } else {
-                    mPinnedStack = stack;
+                    mRootPinnedTask = stack;
                 }
             } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                if (mSplitScreenPrimaryStack != null) {
-                    if (!stack.isDescendantOf(mSplitScreenPrimaryStack)) {
+                if (mRootSplitScreenPrimaryTask != null) {
+                    if (!stack.isDescendantOf(mRootSplitScreenPrimaryTask)) {
                         throw new IllegalArgumentException("addStackReferenceIfNeeded:"
-                                + " split-screen-primary" + " stack=" + mSplitScreenPrimaryStack
+                                + " split-screen-primary" + " stack=" + mRootSplitScreenPrimaryTask
                                 + " already exist on display=" + this + " stack=" + stack);
                     }
                 } else {
-                    mSplitScreenPrimaryStack = stack;
+                    mRootSplitScreenPrimaryTask = stack;
                     mDisplayContent.onSplitScreenModeActivated();
                     mDividerControllerLocked.notifyDockedStackExistsChanged(true);
                 }
@@ -4296,14 +4269,12 @@
         }
 
         void removeStackReferenceIfNeeded(ActivityStack stack) {
-            if (stack == mHomeStack) {
-                mHomeStack = null;
-            } else if (stack == mRecentsStack) {
-                mRecentsStack = null;
-            } else if (stack == mPinnedStack) {
-                mPinnedStack = null;
-            } else if (stack == mSplitScreenPrimaryStack) {
-                mSplitScreenPrimaryStack = null;
+            if (stack == mRootHomeTask) {
+                mRootHomeTask = null;
+            } else if (stack == mRootPinnedTask) {
+                mRootPinnedTask = null;
+            } else if (stack == mRootSplitScreenPrimaryTask) {
+                mRootSplitScreenPrimaryTask = null;
                 mDisplayContent.onSplitScreenModeDismissed();
                 // Re-set the split-screen create mode whenever the split-screen stack is removed.
                 mWmService.setDockedStackCreateStateLocked(
@@ -4405,9 +4376,9 @@
             int minPosition = POSITION_BOTTOM;
 
             if (stack.isAlwaysOnTop()) {
-                if (hasPinnedStack()) {
+                if (hasPinnedTask()) {
                     // Always-on-top stacks go below the pinned stack.
-                    maxPosition = getStacks().indexOf(mPinnedStack) - 1;
+                    maxPosition = getStacks().indexOf(mRootPinnedTask) - 1;
                 }
                 // Always-on-top stacks need to be above all other stacks.
                 minPosition = belowAlwaysOnTopPosition !=
@@ -4532,11 +4503,11 @@
                 // docked stack is visible...except for the home stack if the docked stack is
                 // minimized and it actually set something and the bounds is different from  the
                 // display.
-                if (mHomeStack != null && mHomeStack.isVisible()
+                if (mRootHomeTask != null && mRootHomeTask.isVisible()
                         && mDividerControllerLocked.isMinimizedDock()
                         && !(mDividerControllerLocked.isHomeStackResizable()
-                        && mHomeStack.matchParentBounds())) {
-                    final int orientation = mHomeStack.getOrientation();
+                        && mRootHomeTask.matchParentBounds())) {
+                    final int orientation = mRootHomeTask.getOrientation();
                     if (orientation != SCREEN_ORIENTATION_UNSET) {
                         return orientation;
                     }
@@ -4717,7 +4688,7 @@
         void assignChildLayers(SurfaceControl.Transaction t) {
             mImeWindowsContainers.setNeedsLayer();
             mBelowAppWindowsContainers.assignLayer(t, 0);
-            mTaskStackContainers.assignLayer(t, 1);
+            mTaskContainers.assignLayer(t, 1);
             mAboveAppWindowsContainers.assignLayer(t, 2);
 
             final WindowState imeTarget = mInputMethodTarget;
@@ -4756,7 +4727,7 @@
             // Above we have assigned layers to our children, now we ask them to assign
             // layers to their children.
             mBelowAppWindowsContainers.assignChildLayers(t);
-            mTaskStackContainers.assignChildLayers(t);
+            mTaskContainers.assignChildLayers(t);
             mAboveAppWindowsContainers.assignChildLayers(t);
             mImeWindowsContainers.assignRelativeLayer(t, getSurfaceControl(), Integer.MAX_VALUE);
             mImeWindowsContainers.assignChildLayers(t);
@@ -4769,7 +4740,7 @@
 
         void addChildren() {
             addChild(mBelowAppWindowsContainers, null);
-            addChild(mTaskStackContainers, null);
+            addChild(mTaskContainers, null);
             addChild(mAboveAppWindowsContainers, null);
             addChild(mImeWindowsContainers, null);
         }
@@ -4929,7 +4900,7 @@
         private boolean skipImeWindowsDuringTraversal(DisplayContent dc) {
             // We skip IME windows so they're processed just above their target, except
             // in split-screen mode where we process the IME containers above the docked divider.
-            return dc.mInputMethodTarget != null && !dc.hasSplitScreenPrimaryStack();
+            return dc.mInputMethodTarget != null && !dc.hasSplitScreenPrimaryTask();
         }
 
         /** Like {@link #forAllWindows}, but ignores {@link #skipImeWindowsDuringTraversal} */
@@ -5080,7 +5051,7 @@
     }
 
     void assignStackOrdering() {
-        mTaskStackContainers.assignStackOrdering(getPendingTransaction());
+        mTaskContainers.assignStackOrdering(getPendingTransaction());
     }
 
     /**
@@ -5669,8 +5640,8 @@
             if (currentFocusedStack != prevFocusedStack) {
                 mLastFocusedStack = prevFocusedStack;
                 EventLogTags.writeWmFocusedStack(mRootWindowContainer.mCurrentUser, mDisplayId,
-                        currentFocusedStack == null ? -1 : currentFocusedStack.getStackId(),
-                        mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(),
+                        currentFocusedStack == null ? -1 : currentFocusedStack.getRootTaskId(),
+                        mLastFocusedStack == null ? -1 : mLastFocusedStack.getRootTaskId(),
                         updateLastFocusedStackReason);
             }
         }
@@ -5678,10 +5649,10 @@
         onStackOrderChanged(stack);
     }
 
-    ActivityStack getStack(int stackId) {
+    ActivityStack getStack(int rootTaskId) {
         for (int i = getStackCount() - 1; i >= 0; --i) {
             final ActivityStack stack = getStackAt(i);
-            if (stack.mStackId == stackId) {
+            if (stack.getRootTaskId() == rootTaskId) {
                 return stack;
             }
         }
@@ -6041,7 +6012,7 @@
         } finally {
             final ActivityStack topFullscreenStack =
                     getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
-            final ActivityStack homeStack = getHomeStack();
+            final ActivityStack homeStack = getRootHomeTask();
             if (topFullscreenStack != null && homeStack != null && !isTopStack(homeStack)) {
                 // Whenever split-screen is dismissed we want the home stack directly behind the
                 // current top fullscreen stack so it shows up when the top stack is finished.
@@ -6059,7 +6030,7 @@
         mAtmService.deferWindowLayout();
         try {
             // Adjust the windowing mode of any affected by split-screen to split-screen secondary.
-            final ActivityStack splitScreenPrimaryStack = getSplitScreenPrimaryStack();
+            final ActivityStack splitScreenPrimaryStack = getRootSplitScreenPrimaryTask();
             for (int i = getStackCount() - 1; i >= 0; --i) {
                 final ActivityStack otherStack = getStackAt(i);
                 if (otherStack == splitScreenPrimaryStack
@@ -6188,7 +6159,7 @@
             }
         }
 
-        final boolean inSplitScreenMode = hasSplitScreenPrimaryStack();
+        final boolean inSplitScreenMode = hasSplitScreenPrimaryTask();
         if (!inSplitScreenMode
                 && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) {
             // Switch to the display's windowing mode if we are not in split-screen mode and we are
@@ -6434,7 +6405,7 @@
                     // If default display is in split-window mode, set windowing mode of the stack
                     // to split-screen secondary. Otherwise, set the windowing mode to undefined by
                     // default to let stack inherited the windowing mode from the new display.
-                    final int windowingMode = toDisplay.hasSplitScreenPrimaryStack()
+                    final int windowingMode = toDisplay.hasSplitScreenPrimaryTask()
                             ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
                             : WINDOWING_MODE_UNDEFINED;
                     stack.reparent(toDisplay, true /* onTop */);
@@ -6603,7 +6574,7 @@
     }
 
     void moveHomeStackToFront(String reason) {
-        final ActivityStack homeStack = getHomeStack();
+        final ActivityStack homeStack = getRootHomeTask();
         if (homeStack != null) {
             homeStack.moveToFront(reason);
         }
@@ -6629,7 +6600,7 @@
 
     @Nullable
     ActivityRecord getHomeActivityForUser(int userId) {
-        final ActivityStack homeStack = getHomeStack();
+        final ActivityStack homeStack = getRootHomeTask();
         if (homeStack == null) {
             return null;
         }
@@ -6718,7 +6689,7 @@
 
     public void dumpStacks(PrintWriter pw) {
         for (int i = getStackCount() - 1; i >= 0; --i) {
-            pw.print(getStackAt(i).mStackId);
+            pw.print(getStackAt(i).getRootTaskId());
             if (i > 0) {
                 pw.print(",");
             }
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index b59c4e3..f90f224 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -582,7 +582,7 @@
 
         // In the presence of the PINNED stack or System Alert windows we unfortunately can not
         // seamlessly rotate.
-        if (mDisplayContent.hasPinnedStack() || mDisplayContent.hasAlertWindowSurfaces()) {
+        if (mDisplayContent.hasPinnedTask() || mDisplayContent.hasAlertWindowSurfaces()) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 6b5859d..872379e 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -255,7 +255,7 @@
     }
 
     boolean isHomeStackResizable() {
-        final ActivityStack homeStack = mDisplayContent.getHomeStack();
+        final ActivityStack homeStack = mDisplayContent.getRootHomeTask();
         if (homeStack == null) {
             return false;
         }
@@ -371,7 +371,7 @@
         if (mWindow == null) {
             return;
         }
-        ActivityStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+        ActivityStack stack = mDisplayContent.getRootSplitScreenPrimaryTask();
 
         // If the stack is invisible, we policy force hide it in WindowAnimator.shouldForceHide
         final boolean visible = stack != null;
@@ -415,7 +415,7 @@
     }
 
     void positionDockedStackedDivider(Rect frame) {
-        ActivityStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+        ActivityStack stack = mDisplayContent.getRootSplitScreenPrimaryTask();
         if (stack == null) {
             // Unfortunately we might end up with still having a divider, even though the underlying
             // stack was already removed. This is because we are on AM thread and the removal of the
@@ -524,7 +524,7 @@
             // If a primary stack was just created, it will not have access to display content at
             // this point so pass it from here to get a valid dock side.
             final ActivityStack stack =
-                    mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+                    mDisplayContent.getRootSplitScreenPrimaryTask();
             mOriginalDockedSide = stack.getDockSideForDisplay(mDisplayContent);
             return;
         }
@@ -560,7 +560,7 @@
         long animDuration = 0;
         if (animate) {
             final ActivityStack stack =
-                    mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+                    mDisplayContent.getRootSplitScreenPrimaryTask();
             final long transitionDuration = isAnimationMaximizing()
                     ? mDisplayContent.mAppTransition.getLastClipRevealTransitionDuration()
                     : DEFAULT_APP_TRANSITION_DURATION;
@@ -615,7 +615,7 @@
         mDockedStackListeners.register(listener);
         notifyDockedDividerVisibilityChanged(wasVisible());
         notifyDockedStackExistsChanged(
-                mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility() != null);
+                mDisplayContent.getRootSplitScreenPrimaryTask() != null);
         notifyDockedStackMinimizedChanged(mMinimizedDock, false /* animate */,
                 isHomeStackResizable());
         notifyAdjustedForImeChanged(mAdjustedForIme, 0 /* animDuration */);
@@ -633,7 +633,7 @@
         final ActivityStack stack = targetWindowingMode != WINDOWING_MODE_UNDEFINED
                 ? mDisplayContent.getTopStackInWindowingMode(targetWindowingMode)
                 : null;
-        final ActivityStack dockedStack = mDisplayContent.getSplitScreenPrimaryStack();
+        final ActivityStack dockedStack = mDisplayContent.getRootSplitScreenPrimaryTask();
         boolean visibleAndValid = visible && stack != null && dockedStack != null;
 
         // Ensure an old dim that was shown for the docked stack divider is removed so we don't end
@@ -701,10 +701,10 @@
     }
 
     void checkMinimizeChanged(boolean animate) {
-        if (mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility() == null) {
+        if (mDisplayContent.getRootSplitScreenPrimaryTask() == null) {
             return;
         }
-        final ActivityStack homeStack = mDisplayContent.getHomeStack();
+        final ActivityStack homeStack = mDisplayContent.getRootHomeTask();
         if (homeStack == null) {
             return;
         }
@@ -873,7 +873,7 @@
     }
 
     private boolean setMinimizedDockedStack(boolean minimized) {
-        final ActivityStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+        final ActivityStack stack = mDisplayContent.getRootSplitScreenPrimaryTask();
         notifyDockedStackMinimizedChanged(minimized, false /* animate */, isHomeStackResizable());
         return stack != null && stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f);
     }
@@ -923,7 +923,7 @@
     }
 
     private boolean animateForMinimizedDockedStack(long now) {
-        final ActivityStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+        final ActivityStack stack = mDisplayContent.getRootSplitScreenPrimaryTask();
         if (!mAnimationStarted) {
             mAnimationStarted = true;
             mAnimationStartTime = now;
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 7db30f6..ad65e10 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -407,7 +407,7 @@
             // stack since it will be complicated/risky to try to put the activity on top
             // of the lock screen in the right fullscreen configuration.
             final ActivityStack stack =
-                    mRootWindowContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
+                    mRootWindowContainer.getDefaultDisplay().getRootSplitScreenPrimaryTask();
             if (stack == null) {
                 return;
             }
diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java
index 03e1322..a9820ef 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsController.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsController.java
@@ -138,7 +138,7 @@
         try {
             if (mTmpParams.hasPreferredDisplay()
                     && mTmpParams.mPreferredDisplayId != task.getStack().getDisplay().mDisplayId) {
-                mService.moveStackToDisplay(task.getStackId(), mTmpParams.mPreferredDisplayId);
+                mService.moveStackToDisplay(task.getRootTaskId(), mTmpParams.mPreferredDisplayId);
             }
 
             if (mTmpParams.hasWindowingMode()
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index b4f75e5..1d5b5d1 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -125,7 +125,7 @@
         public void startAnimation(Rect destinationBounds, Rect sourceRectHint,
                 int animationDuration) {
             synchronized (mService.mGlobalLock) {
-                final ActivityStack pinnedStack = mDisplayContent.getPinnedStack();
+                final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask();
                 pinnedStack.animateResizePinnedStack(destinationBounds,
                         sourceRectHint, animationDuration, true /* fromFullscreen */);
             }
@@ -134,7 +134,7 @@
         @Override
         public void resetBoundsAnimation(Rect bounds) {
             synchronized (mService.mGlobalLock) {
-                if (mDisplayContent.hasPinnedStack()) {
+                if (mDisplayContent.hasPinnedTask()) {
                     final ActivityStack pinnedStack = mDisplayContent.getTopStackInWindowingMode(
                             WINDOWING_MODE_PINNED);
                     if (pinnedStack != null) {
@@ -383,7 +383,7 @@
             }
             try {
                 final Rect animatingBounds = new Rect();
-                final ActivityStack pinnedStack = mDisplayContent.getPinnedStack();
+                final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask();
                 if (pinnedStack != null) {
                     pinnedStack.getAnimationOrCurrentBounds(animatingBounds);
                 }
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index e1dfc17..292e8aa 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -1387,7 +1387,7 @@
 
         // Trim tasks that are in stacks that are behind the home stack
         final DisplayContent display = stack.getDisplay();
-        return display.getIndexOf(stack) < display.getIndexOf(display.getHomeStack());
+        return display.getIndexOf(stack) < display.getIndexOf(display.getRootHomeTask());
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 6148095..da9d074 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -413,7 +413,7 @@
 
         // Save the minimized home height
         final ActivityStack dockedStack =
-                mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+                mDisplayContent.getRootSplitScreenPrimaryTask();
         mDisplayContent.getDockedDividerController().getHomeStackBoundsInDockedMode(
                 mDisplayContent.getConfiguration(),
                 dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide(),
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 8202833..d3da500 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1656,7 +1656,7 @@
         // Only resume home activity if isn't finishing.
         if (r != null && !r.finishing) {
             r.moveFocusableActivityToTop(myReason);
-            return resumeFocusedStacksTopActivities(r.getActivityStack(), prev, null);
+            return resumeFocusedStacksTopActivities(r.getRootTask(), prev, null);
         }
         return startHomeOnDisplay(mCurrentUser, myReason, displayId);
     }
@@ -1980,9 +1980,9 @@
     }
 
     boolean switchUser(int userId, UserState uss) {
-        final int focusStackId = getTopDisplayFocusedStack().getStackId();
+        final int focusStackId = getTopDisplayFocusedStack().getRootTaskId();
         // We dismiss the docked stack whenever we switch users.
-        final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenPrimaryStack();
+        final ActivityStack dockedStack = getDefaultDisplay().getRootSplitScreenPrimaryTask();
         if (dockedStack != null) {
             mStackSupervisor.moveTasksToFullscreenStackLocked(
                     dockedStack, dockedStack.isFocusedStackOnDisplay());
@@ -1994,7 +1994,7 @@
 
         mUserStackInFront.put(mCurrentUser, focusStackId);
         final int restoreStackId =
-                mUserStackInFront.get(userId, getDefaultDisplay().getHomeStack().mStackId);
+                mUserStackInFront.get(userId, getDefaultDisplay().getRootHomeTask().getRootTaskId());
         mCurrentUser = userId;
 
         mStackSupervisor.mStartingUsers.add(uss);
@@ -2012,7 +2012,7 @@
 
         ActivityStack stack = getStack(restoreStackId);
         if (stack == null) {
-            stack = getDefaultDisplay().getHomeStack();
+            stack = getDefaultDisplay().getRootHomeTask();
         }
         final boolean homeInFront = stack.isActivityTypeHome();
         if (stack.isOnHomeDisplay()) {
@@ -2034,8 +2034,8 @@
      */
     void updateUserStack(int userId, ActivityStack stack) {
         if (userId != mCurrentUser) {
-            mUserStackInFront.put(userId, stack != null ? stack.getStackId()
-                    : getDefaultDisplay().getHomeStack().mStackId);
+            mUserStackInFront.put(userId, stack != null ? stack.getRootTaskId()
+                    : getDefaultDisplay().getRootHomeTask().getRootTaskId());
         }
     }
 
@@ -2108,12 +2108,12 @@
             String reason) {
         mService.deferWindowLayout();
 
-        final DisplayContent display = r.getActivityStack().getDisplay();
+        final DisplayContent display = r.getRootTask().getDisplay();
 
         try {
             final Task task = r.getTask();
 
-            final ActivityStack pinnedStack = display.getPinnedStack();
+            final ActivityStack pinnedStack = display.getRootPinnedTask();
             // This will change the pinned stack's windowing mode to its original mode, ensuring
             // we only have one stack that is in pinned mode.
             if (pinnedStack != null) {
@@ -2124,13 +2124,13 @@
 
             final ActivityStack stack;
             if (singleActivity) {
-                stack = r.getActivityStack();
+                stack = r.getRootTask();
             } else {
                 // In the case of multiple activities, we will create a new stack for it and then
                 // move the PIP activity into the stack.
                 // We will then perform a windowing mode change for both scenarios.
                 stack = display.createStack(
-                        r.getActivityStack().getRequestedOverrideWindowingMode(),
+                        r.getRootTask().getRequestedOverrideWindowingMode(),
                         r.getActivityType(), ON_TOP, r.info, r.intent);
                 // There are multiple activities in the task and moving the top activity should
                 // reveal/leave the other activities in their original task.
@@ -2398,7 +2398,7 @@
         ActivityManager.StackInfo info = new ActivityManager.StackInfo();
         stack.getBounds(info.bounds);
         info.displayId = display.mDisplayId;
-        info.stackId = stack.mStackId;
+        info.stackId = stack.mTaskId;
         info.stackToken = stack.mRemoteToken;
         info.userId = stack.mCurrentUser;
         info.visible = stack.shouldBeVisible(null);
@@ -2853,7 +2853,7 @@
             stack = candidateTask.getStack();
         }
         if (stack == null && r != null) {
-            stack = r.getActivityStack();
+            stack = r.getRootTask();
         }
         if (stack != null) {
             display = stack.getDisplay();
@@ -2868,7 +2868,7 @@
                     return stack;
                 }
                 if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
-                        && display.getSplitScreenPrimaryStack() == stack
+                        && display.getRootSplitScreenPrimaryTask() == stack
                         && candidateTask == stack.getTopMostTask()) {
                     // This is a special case when we try to launch an activity that is currently on
                     // top of split-screen primary stack, but is targeting split-screen secondary.
@@ -3244,7 +3244,7 @@
             if (resumedActivity == null || !resumedActivity.idle) {
                 if (DEBUG_STATES) {
                     Slog.d(TAG_STATES, "allResumedActivitiesIdle: stack="
-                            + stack.mStackId + " " + resumedActivity + " not idle");
+                            + stack.getRootTaskId() + " " + resumedActivity + " not idle");
                 }
                 return false;
             }
@@ -3599,7 +3599,7 @@
         // TODO(b/111541062): Update tests to look for resumed activities on all displays
         final ActivityStack focusedStack = getTopDisplayFocusedStack();
         if (focusedStack != null) {
-            proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
+            proto.write(FOCUSED_STACK_ID, focusedStack.getRootTaskId());
             final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
             if (focusedActivity != null) {
                 focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 9e6cb68..c52966b 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -526,7 +526,7 @@
             ActivityStack stack) {
         super(atmService.mWindowManager);
 
-        EventLogTags.writeWmTaskCreated(_taskId, stack != null ? stack.mStackId : INVALID_STACK_ID);
+        EventLogTags.writeWmTaskCreated(_taskId, stack != null ? getRootTaskId() : INVALID_TASK_ID);
         mAtmService = atmService;
         mStackSupervisor = atmService.mStackSupervisor;
         mRootWindowContainer = mAtmService.mRootWindowContainer;
@@ -1080,7 +1080,7 @@
         // the display, so we should probably consolidate it there instead.
 
         if (getParent() == null && mDisplayContent != null) {
-            EventLogTags.writeWmStackRemoved(getStackId());
+            EventLogTags.writeWmStackRemoved(getRootTaskId());
             mDisplayContent = null;
             mWmService.mWindowPlacerLocked.requestTraversal();
         }
@@ -2344,17 +2344,27 @@
         return dc != null ? dc.mDisplayId : INVALID_DISPLAY;
     }
 
+    // TODO: Migrate callers to getRootTask()
     ActivityStack getStack() {
-        final WindowContainer parent = getParent();
-        return (ActivityStack) (parent instanceof ActivityStack ? parent : this);
+        return (ActivityStack) getRootTask();
     }
 
-    /**
-     * @return Id of current stack, {@link ActivityTaskManager#INVALID_STACK_ID} if no stack is set.
-     */
-    int getStackId() {
-        final ActivityStack stack = getStack();
-        return stack != null ? stack.mStackId : INVALID_STACK_ID;
+    /** @return Id of root task. */
+    int getRootTaskId() {
+        return getRootTask().mTaskId;
+    }
+
+    Task getRootTask() {
+        final WindowContainer parent = getParent();
+        if (parent == null) return this;
+
+        final Task parentTask = parent.asTask();
+        return parentTask == null ? this : parentTask.getRootTask();
+    }
+
+    // TODO(task-merge): Figure out what's the right thing to do for places that used it.
+    boolean isRootTask() {
+        return getRootTask() == this;
     }
 
     int getDescendantTaskCount() {
@@ -2736,7 +2746,7 @@
             // No need to check if the mode is allowed if it's leaving dragResize
             if (dragResizing && !DragResizeMode.isModeAllowedForStack(getStack(), dragResizeMode)) {
                 throw new IllegalArgumentException("Drag resize mode not allow for stack stackId="
-                        + getStack().mStackId + " dragResizeMode=" + dragResizeMode);
+                        + getRootTaskId() + " dragResizeMode=" + dragResizeMode);
             }
             mDragResizing = dragResizing;
             mDragResizeMode = dragResizeMode;
@@ -3010,11 +3020,6 @@
         return mTaskDescription;
     }
 
-    // TODO(task-merge): Figure out what's the right thing to do for places that used it.
-    boolean isRootTask() {
-        return getParent() == null || getParent().asTask() == null;
-    }
-
     @Override
     boolean fillsParent() {
         return matchParentBounds();
@@ -3167,7 +3172,7 @@
     void fillTaskInfo(TaskInfo info) {
         getNumRunningActivities(mReuseActivitiesReport);
         info.userId = mUserId;
-        info.stackId = getStackId();
+        info.stackId = getRootTaskId();
         info.taskId = mTaskId;
         info.displayId = getDisplayId();
         info.isRunning = getTopNonFinishingActivity() != null;
@@ -3298,7 +3303,7 @@
         if (mRootProcess != null) {
             pw.print(prefix); pw.print("mRootProcess="); pw.println(mRootProcess);
         }
-        pw.print(prefix); pw.print("taskId=" + mTaskId); pw.println(" stackId=" + getStackId());
+        pw.print(prefix); pw.print("taskId=" + mTaskId); pw.println(" stackId=" + getRootTaskId());
         pw.print(prefix + "hasBeenVisible=" + hasBeenVisible);
         pw.print(" mResizeMode=" + ActivityInfo.resizeModeToString(mResizeMode));
         pw.print(" mSupportsPictureInPicture=" + mSupportsPictureInPicture);
@@ -3315,7 +3320,7 @@
             sb.append(" U=");
             sb.append(mUserId);
             sb.append(" StackId=");
-            sb.append(getStackId());
+            sb.append(getRootTaskId());
             sb.append(" sz=");
             sb.append(getChildCount());
             sb.append('}');
@@ -3358,7 +3363,7 @@
         forAllActivities((r) -> {
             r.dumpDebug(proto, ACTIVITIES);
         });
-        proto.write(STACK_ID, getStackId());
+        proto.write(STACK_ID, getRootTaskId());
         if (mLastNonFullscreenBounds != null) {
             mLastNonFullscreenBounds.dumpDebug(proto, LAST_NON_FULLSCREEN_BOUNDS);
         }
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 688fe12..2dde0ba 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -350,7 +350,7 @@
     void notifyActivityPinned(ActivityRecord r) {
         mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG);
         final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG,
-                r.getTask().mTaskId, r.getStackId(), r.packageName);
+                r.getTask().mTaskId, r.getRootTaskId(), r.packageName);
         msg.sendingUid = r.mUserId;
         forAllLocalListeners(mNotifyActivityPinned, msg);
         msg.sendToTarget();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index cd5da08..81a4c68 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3163,7 +3163,7 @@
             // Notify whether the docked stack exists for the current user
             final DisplayContent displayContent = getDefaultDisplayContentLocked();
             final ActivityStack stack =
-                    displayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+                    displayContent.getRootSplitScreenPrimaryTask();
             displayContent.mDividerControllerLocked.notifyDockedStackExistsChanged(
                     stack != null && stack.hasTaskForUser(newUserId));
 
@@ -6414,7 +6414,7 @@
     public int getDockedStackSide() {
         synchronized (mGlobalLock) {
             final ActivityStack dockedStack = getDefaultDisplayContentLocked()
-                    .getSplitScreenPrimaryStackIgnoringVisibility();
+                    .getRootSplitScreenPrimaryTask();
             return dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide();
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index c755e60..812942e 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -618,15 +618,15 @@
 
         // Compare the z-order of ActivityStacks if both activities landed on same display.
         if (display == topDisplay
-                && mPreQTopResumedActivity.getActivityStack().compareTo(
-                        activity.getActivityStack()) <= 0) {
+                && mPreQTopResumedActivity.getRootTask().compareTo(
+                        activity.getRootTask()) <= 0) {
             canUpdate = true;
         }
 
         if (canUpdate) {
             // Make sure the previous top activity in the process no longer be resumed.
             if (mPreQTopResumedActivity != null && mPreQTopResumedActivity.isState(RESUMED)) {
-                final ActivityStack stack = mPreQTopResumedActivity.getActivityStack();
+                final ActivityStack stack = mPreQTopResumedActivity.getRootTask();
                 if (stack != null) {
                     stack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
                             activity);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 2965764..73984fd 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -16,7 +16,7 @@
 
 package com.android.server.wm;
 
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_DEFAULT;
 import static android.app.AppOpsManager.OP_NONE;
@@ -1055,7 +1055,7 @@
                 }
             }
 
-            final ActivityStack stack = getStack();
+            final ActivityStack stack = getRootTask();
             if (inPinnedWindowingMode() && stack != null
                     && stack.lastAnimatingBoundsWasToFullscreen()) {
                 // PIP edge case: When going from pinned to fullscreen, we apply a
@@ -1469,17 +1469,15 @@
         return mActivityRecord != null ? mActivityRecord.getTask() : null;
     }
 
-    ActivityStack getStack() {
-        Task task = getTask();
+    ActivityStack getRootTask() {
+        final Task task = getTask();
         if (task != null) {
-            if (task.getStack() != null) {
-                return task.getStack();
-            }
+            return (ActivityStack) task.getRootTask();
         }
         // Some system windows (e.g. "Power off" dialog) don't have a task, but we would still
         // associate them with some stack to enable dimming.
         final DisplayContent dc = getDisplayContent();
-        return mAttrs.type >= FIRST_SYSTEM_WINDOW && dc != null ? dc.getHomeStack() : null;
+        return mAttrs.type >= FIRST_SYSTEM_WINDOW && dc != null ? dc.getRootHomeTask() : null;
     }
 
     /**
@@ -2229,7 +2227,7 @@
             return false;
         }
 
-        final ActivityStack stack = getStack();
+        final ActivityStack stack = getRootTask();
         if (stack != null && stack.shouldIgnoreInput()) {
             // Ignore when the stack shouldn't receive input event.
             // (i.e. the minimized stack in split screen mode.)
@@ -2472,7 +2470,7 @@
             if (task != null) {
                 task.getDimBounds(mTmpRect);
             } else {
-                getStack().getDimBounds(mTmpRect);
+                getRootTask().getDimBounds(mTmpRect);
             }
         }
         if (inFreeformWindowingMode()) {
@@ -2653,7 +2651,7 @@
                             // also reset drag resizing state, because the owner can't do it
                             // anymore.
                             final ActivityStack stack =
-                                    dc.getSplitScreenPrimaryStackIgnoringVisibility();
+                                    dc.getRootSplitScreenPrimaryTask();
                             if (stack != null) {
                                 stack.resetDockedStackToMiddle();
                             }
@@ -3563,12 +3561,12 @@
         return mTmpRect;
     }
 
-    private int getStackId() {
-        final ActivityStack stack = getStack();
+    private int getRootTaskId() {
+        final ActivityStack stack = getRootTask();
         if (stack == null) {
-            return INVALID_STACK_ID;
+            return INVALID_TASK_ID;
         }
-        return stack.mStackId;
+        return stack.mTaskId;
     }
 
     private void dispatchResized(Rect frame, Rect contentInsets,
@@ -3753,7 +3751,7 @@
         super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
         writeIdentifierToProto(proto, IDENTIFIER);
         proto.write(DISPLAY_ID, getDisplayId());
-        proto.write(STACK_ID, getStackId());
+        proto.write(STACK_ID, getRootTaskId());
         mAttrs.dumpDebug(proto, ATTRIBUTES);
         mGivenContentInsets.dumpDebug(proto, GIVEN_CONTENT_INSETS);
         mWindowFrames.dumpDebug(proto, WINDOW_FRAMES);
@@ -3795,10 +3793,9 @@
 
     @Override
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
-        final ActivityStack stack = getStack();
         pw.print(prefix + "mDisplayId=" + getDisplayId());
-        if (stack != null) {
-            pw.print(" stackId=" + stack.mStackId);
+        if (getRootTask() != null) {
+            pw.print(" rootTaskId=" + getRootTaskId());
         }
         pw.println(" mSession=" + mSession
                 + " mClient=" + mClient.asBinder());
@@ -5271,7 +5268,7 @@
             outPoint.offset(-parentBounds.left, -parentBounds.top);
         }
 
-        ActivityStack stack = getStack();
+        ActivityStack stack = getRootTask();
 
         // If we have stack outsets, that means the top-left
         // will be outset, and we need to inset ourselves
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 6189fbd..069ee4f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1013,7 +1013,7 @@
                         mSurfaceController.deferTransactionUntil(mSurfaceController.mSurfaceControl,
                                 mWin.getFrameNumber());
                     } else {
-                        final ActivityStack stack = mWin.getStack();
+                        final ActivityStack stack = mWin.getRootTask();
                         mTmpPos.x = 0;
                         mTmpPos.y = 0;
                         if (stack != null) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4b90027..e939d84 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -9102,11 +9102,16 @@
     }
 
     private void enforceAcrossUsersPermissions() {
-        if (isCallerWithSystemUid() || mInjector.binderGetCallingUid() == Process.ROOT_UID) {
+        final int callingUid = mInjector.binderGetCallingUid();
+        final int callingPid = mInjector.binderGetCallingPid();
+        final String packageName = mContext.getPackageName();
+
+        if (isCallerWithSystemUid() || callingUid == Process.ROOT_UID) {
             return;
         }
-        if (mContext.checkCallingPermission(permission.INTERACT_ACROSS_PROFILES)
-                == PackageManager.PERMISSION_GRANTED) {
+        if (PermissionChecker.checkPermissionForPreflight(
+                mContext, permission.INTERACT_ACROSS_PROFILES, callingPid, callingUid,
+                packageName) == PermissionChecker.PERMISSION_GRANTED) {
             return;
         }
         if (mContext.checkCallingPermission(permission.INTERACT_ACROSS_USERS)
diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
index ce088643..96ff9c1 100644
--- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
+++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
@@ -79,6 +79,7 @@
 @Config(shadows = {ShadowUserManager.class, ShadowApplicationPackageManager.class})
 public class CrossProfileAppsServiceImplRoboTest {
     private static final int CALLING_UID = 1111;
+    private static final int CALLING_PID = 1000;
     private static final String CROSS_PROFILE_APP_PACKAGE_NAME =
             "com.android.server.pm.crossprofileappsserviceimplrobotest.crossprofileapp";
     private static final int PERSONAL_PROFILE_USER_ID = 0;
@@ -383,6 +384,11 @@
         }
 
         @Override
+        public int getCallingPid() {
+            return CALLING_PID;
+        }
+
+        @Override
         public @UserIdInt int getCallingUserId() {
             return PERSONAL_PROFILE_USER_ID;
         }
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 43e9570..aeba488 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5711,6 +5711,7 @@
 
     public void testGetAllCrossProfilePackages_notSet_returnsEmpty() throws Exception {
         addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1);
+        mContext.packageName = admin1.getPackageName();
 
         setCrossProfileAppsList();
 
@@ -5720,6 +5721,7 @@
     public void testGetAllCrossProfilePackages_notSet_dpmsReinitialized_returnsEmpty()
             throws Exception {
         addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1);
+        mContext.packageName = admin1.getPackageName();
 
         setCrossProfileAppsList();
         initializeDpms();
@@ -5730,6 +5732,7 @@
     public void testGetAllCrossProfilePackages_whenSet_returnsCombinedSet() throws Exception {
         addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1);
         final Set<String> packages = Sets.newSet("TEST_PACKAGE", "TEST_COMMON_PACKAGE");
+        mContext.packageName = admin1.getPackageName();
 
         dpm.setCrossProfilePackages(admin1, packages);
         setCrossProfileAppsList("TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE");
@@ -5744,6 +5747,7 @@
             throws Exception {
         addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1);
         final Set<String> packages = Sets.newSet("TEST_PACKAGE", "TEST_COMMON_PACKAGE");
+        mContext.packageName = admin1.getPackageName();
 
         dpm.setCrossProfilePackages(admin1, packages);
         setCrossProfileAppsList("TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE");
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 960f670..6cf6b67 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -219,6 +219,8 @@
                 return mMockSystemServices.accountManager;
             case Context.TELEPHONY_SERVICE:
                 return mMockSystemServices.telephonyManager;
+            case Context.APP_OPS_SERVICE:
+                return mMockSystemServices.appOpsManager;
         }
         throw new UnsupportedOperationException();
     }
@@ -256,6 +258,22 @@
     }
 
     @Override
+    public int checkPermission(String permission, int pid, int uid) {
+        if (UserHandle.isSameApp(binder.getCallingUid(), SYSTEM_UID)) {
+            return PackageManager.PERMISSION_GRANTED; // Assume system has all permissions.
+        }
+        List<String> permissions = binder.callingPermissions.get(binder.getCallingUid());
+        if (permissions == null) {
+            permissions = callerPermissions;
+        }
+        if (permissions.contains(permission)) {
+            return PackageManager.PERMISSION_GRANTED;
+        } else {
+            return PackageManager.PERMISSION_DENIED;
+        }
+    }
+
+    @Override
     public void sendBroadcast(Intent intent) {
         spiedContext.sendBroadcast(intent);
     }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 068daf5..b9fb1aa 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -28,6 +28,7 @@
 import android.accounts.AccountManager;
 import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
+import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.IActivityTaskManager;
 import android.app.NotificationManager;
@@ -120,6 +121,7 @@
     public final TimeZoneDetector timeZoneDetector;
     public final KeyChain.KeyChainConnection keyChainConnection;
     public final PersistentDataBlockManagerInternal persistentDataBlockManagerInternal;
+    public final AppOpsManager appOpsManager;
     /** Note this is a partial mock, not a real mock. */
     public final PackageManager packageManager;
     public final BuildMock buildMock = new BuildMock();
@@ -164,6 +166,7 @@
         timeZoneDetector = mock(TimeZoneDetector.class);
         keyChainConnection = mock(KeyChain.KeyChainConnection.class, RETURNS_DEEP_STUBS);
         persistentDataBlockManagerInternal = mock(PersistentDataBlockManagerInternal.class);
+        appOpsManager = mock(AppOpsManager.class);
 
         // Package manager is huge, so we use a partial mock instead.
         packageManager = spy(realContext.getPackageManager());
diff --git a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
index 68f60b4..91cc9f3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
@@ -27,6 +27,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.PermissionInfo;
 import android.content.pm.ResolveInfo;
 import android.os.Bundle;
 import android.os.UserHandle;
@@ -95,6 +96,7 @@
     public void initCrossProfileAppsServiceImpl() {
         mTestInjector = new TestInjector();
         mCrossProfileAppsServiceImpl = new CrossProfileAppsServiceImpl(mContext, mTestInjector);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
     }
 
     @Before
@@ -365,6 +367,11 @@
 
     @Test
     public void startAnyActivityAsUser_profile_notExported() {
+        try {
+            when(mPackageManager.getPermissionInfo(anyString(), anyInt()))
+                    .thenReturn(new PermissionInfo());
+        } catch (PackageManager.NameNotFoundException ignored) {
+        }
         mActivityInfo.exported = false;
 
         assertThrows(
@@ -523,11 +530,16 @@
     private class TestInjector implements CrossProfileAppsServiceImpl.Injector {
         private int mCallingUid;
         private int mCallingUserId;
+        private int mCallingPid;
 
         public void setCallingUid(int uid) {
             mCallingUid = uid;
         }
 
+        public void setCallingPid(int pid) {
+            mCallingPid = pid;
+        }
+
         public void setCallingUserId(int userId) {
             mCallingUserId = userId;
         }
@@ -538,6 +550,11 @@
         }
 
         @Override
+        public int getCallingPid() {
+            return mCallingPid;
+        }
+
+        @Override
         public int getCallingUserId() {
             return mCallingUserId;
         }
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index 03c10f3..22046a5 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -16,38 +16,54 @@
 
 package com.android.server;
 
+import android.app.AlarmManager;
 import android.app.IUiModeManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.os.Handler;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import com.android.server.twilight.TwilightManager;
+import com.android.server.twilight.TwilightState;
 import com.android.server.wm.WindowManagerInternal;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 
-import java.util.HashSet;
-import java.util.Set;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
 
 import static android.app.UiModeManager.MODE_NIGHT_AUTO;
+import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
 import static android.app.UiModeManager.MODE_NIGHT_NO;
 import static android.app.UiModeManager.MODE_NIGHT_YES;
 import static junit.framework.TestCase.assertFalse;
 import static junit.framework.TestCase.assertTrue;
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
 import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
 
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -66,22 +82,51 @@
     TwilightManager mTwilightManager;
     @Mock
     PowerManager.WakeLock mWakeLock;
-    private Set<BroadcastReceiver> mScreenOffRecievers;
+    @Mock
+    AlarmManager mAlarmManager;
+    @Mock
+    PowerManager mPowerManager;
+    @Mock
+    TwilightState mTwilightState;
+
+    private BroadcastReceiver mScreenOffCallback;
+    private BroadcastReceiver mTimeChangedCallback;
+    private AlarmManager.OnAlarmListener mCustomListener;
 
     @Before
     public void setUp() {
-        mUiManagerService = new UiModeManagerService(mContext, mWindowManager, mWakeLock,
-                mTwilightManager, true);
-        mScreenOffRecievers = new HashSet<>();
+        initMocks(this);
+        mUiManagerService = new UiModeManagerService(mContext,
+                mWindowManager, mAlarmManager, mPowerManager,
+                mWakeLock, mTwilightManager, true);
         mService = mUiManagerService.getService();
         when(mContext.checkCallingOrSelfPermission(anyString()))
                 .thenReturn(PackageManager.PERMISSION_GRANTED);
         when(mContext.getResources()).thenReturn(mResources);
         when(mContext.getContentResolver()).thenReturn(mContentResolver);
-        when(mContext.registerReceiver(any(), any())).then(inv -> {
-            mScreenOffRecievers.add(inv.getArgument(0));
+        when(mPowerManager.isInteractive()).thenReturn(true);
+        when(mTwilightManager.getLastTwilightState()).thenReturn(mTwilightState);
+        when(mTwilightState.isNight()).thenReturn(true);
+        when(mContext.registerReceiver(notNull(), notNull())).then(inv -> {
+            IntentFilter filter = inv.getArgument(1);
+            if (filter.hasAction(Intent.ACTION_TIMEZONE_CHANGED)) {
+                mTimeChangedCallback = inv.getArgument(0);
+            }
+            if (filter.hasAction(Intent.ACTION_SCREEN_OFF)) {
+                mScreenOffCallback = inv.getArgument(0);
+            }
             return null;
         });
+        doAnswer(inv -> {
+            mCustomListener = inv.getArgument(3);
+            return null;
+        }).when(mAlarmManager).setExact(anyInt(), anyLong(), anyString(),
+                any(AlarmManager.OnAlarmListener.class), any(Handler.class));
+
+        doAnswer(inv -> {
+            mCustomListener = () -> {};
+            return null;
+        }).when(mAlarmManager).cancel(eq(mCustomListener));
     }
 
     @Test
@@ -102,7 +147,7 @@
             mService.setNightMode(MODE_NIGHT_NO);
         } catch (SecurityException e) { /*we should ignore this update config exception*/ }
         given(mContext.registerReceiver(any(), any())).willThrow(SecurityException.class);
-        verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
+        verify(mContext, atLeastOnce()).unregisterReceiver(any(BroadcastReceiver.class));
     }
 
     @Test
@@ -165,6 +210,132 @@
         assertFalse(isNightModeActivated());
     }
 
+    @Test
+    public void customTime_darkThemeOn() throws RemoteException {
+        LocalTime now = LocalTime.now();
+        mService.setNightMode(MODE_NIGHT_NO);
+        mService.setCustomNightModeStart(now.minusHours(1L).toNanoOfDay() / 1000);
+        mService.setCustomNightModeEnd(now.plusHours(1L).toNanoOfDay() / 1000);
+        mService.setNightMode(MODE_NIGHT_CUSTOM);
+        mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
+        assertTrue(isNightModeActivated());
+    }
+
+    @Test
+    public void customTime_darkThemeOff() throws RemoteException {
+        LocalTime now = LocalTime.now();
+        mService.setNightMode(MODE_NIGHT_YES);
+        mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000);
+        mService.setCustomNightModeEnd(now.minusHours(1L).toNanoOfDay() / 1000);
+        mService.setNightMode(MODE_NIGHT_CUSTOM);
+        mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
+        assertFalse(isNightModeActivated());
+    }
+
+    @Test
+    public void customTime_darkThemeOff_afterStartEnd() throws RemoteException {
+        LocalTime now = LocalTime.now();
+        mService.setNightMode(MODE_NIGHT_YES);
+        mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000);
+        mService.setCustomNightModeEnd(now.plusHours(2L).toNanoOfDay() / 1000);
+        mService.setNightMode(MODE_NIGHT_CUSTOM);
+        mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
+        assertFalse(isNightModeActivated());
+    }
+
+    @Test
+    public void customTime_darkThemeOn_afterStartEnd() throws RemoteException {
+        LocalTime now = LocalTime.now();
+        mService.setNightMode(MODE_NIGHT_YES);
+        mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000);
+        mService.setCustomNightModeEnd(now.plusHours(2L).toNanoOfDay() / 1000);
+        mService.setNightMode(MODE_NIGHT_CUSTOM);
+        mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
+        assertFalse(isNightModeActivated());
+    }
+
+
+
+    @Test
+    public void customTime_darkThemeOn_beforeStartEnd() throws RemoteException {
+        LocalTime now = LocalTime.now();
+        mService.setNightMode(MODE_NIGHT_YES);
+        mService.setCustomNightModeStart(now.minusHours(1L).toNanoOfDay() / 1000);
+        mService.setCustomNightModeEnd(now.minusHours(2L).toNanoOfDay() / 1000);
+        mService.setNightMode(MODE_NIGHT_CUSTOM);
+        mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
+        assertTrue(isNightModeActivated());
+    }
+
+    @Test
+    public void customTime_darkThemeOff_beforeStartEnd() throws RemoteException {
+        LocalTime now = LocalTime.now();
+        mService.setNightMode(MODE_NIGHT_YES);
+        mService.setCustomNightModeStart(now.minusHours(2L).toNanoOfDay() / 1000);
+        mService.setCustomNightModeEnd(now.minusHours(1L).toNanoOfDay() / 1000);
+        mService.setNightMode(MODE_NIGHT_CUSTOM);
+        mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
+        assertFalse(isNightModeActivated());
+    }
+
+    @Test
+    public void customTIme_customAlarmSetWhenScreenTimeChanges() throws RemoteException {
+        when(mPowerManager.isInteractive()).thenReturn(false);
+        mService.setNightMode(MODE_NIGHT_CUSTOM);
+        verify(mAlarmManager, times(1))
+                .setExact(anyInt(), anyLong(), anyString(), any(), any());
+        mTimeChangedCallback.onReceive(mContext, new Intent(Intent.ACTION_TIME_CHANGED));
+        verify(mAlarmManager, atLeast(2))
+                .setExact(anyInt(), anyLong(), anyString(), any(), any());
+    }
+
+    @Test
+    public void customTime_alarmSetInTheFutureWhenOn() throws RemoteException {
+        LocalDateTime now = LocalDateTime.now();
+        when(mPowerManager.isInteractive()).thenReturn(false);
+        mService.setNightMode(MODE_NIGHT_YES);
+        mService.setCustomNightModeStart(now.toLocalTime().minusHours(1L).toNanoOfDay() / 1000);
+        mService.setCustomNightModeEnd(now.toLocalTime().plusHours(1L).toNanoOfDay() / 1000);
+        LocalDateTime next = now.plusHours(1L);
+        final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
+        mService.setNightMode(MODE_NIGHT_CUSTOM);
+        verify(mAlarmManager)
+                .setExact(anyInt(), eq(millis), anyString(), any(), any());
+    }
+
+    @Test
+    public void customTime_appliesImmediatelyWhenScreenOff() throws RemoteException {
+        when(mPowerManager.isInteractive()).thenReturn(false);
+        LocalTime now = LocalTime.now();
+        mService.setNightMode(MODE_NIGHT_NO);
+        mService.setCustomNightModeStart(now.minusHours(1L).toNanoOfDay() / 1000);
+        mService.setCustomNightModeEnd(now.plusHours(1L).toNanoOfDay() / 1000);
+        mService.setNightMode(MODE_NIGHT_CUSTOM);
+        assertTrue(isNightModeActivated());
+    }
+
+    @Test
+    public void customTime_appliesOnlyWhenScreenOff() throws RemoteException {
+        LocalTime now = LocalTime.now();
+        mService.setNightMode(MODE_NIGHT_NO);
+        mService.setCustomNightModeStart(now.minusHours(1L).toNanoOfDay() / 1000);
+        mService.setCustomNightModeEnd(now.plusHours(1L).toNanoOfDay() / 1000);
+        mService.setNightMode(MODE_NIGHT_CUSTOM);
+        assertFalse(isNightModeActivated());
+        mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
+        assertTrue(isNightModeActivated());
+    }
+
+    @Test
+    public void nightAuto_appliesOnlyWhenScreenOff() throws RemoteException {
+        when(mTwilightState.isNight()).thenReturn(true);
+        mService.setNightMode(MODE_NIGHT_NO);
+        mService.setNightMode(MODE_NIGHT_AUTO);
+        assertFalse(isNightModeActivated());
+        mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
+        assertTrue(isNightModeActivated());
+    }
+
     private boolean isNightModeActivated() {
         return (mUiManagerService.getConfiguration().uiMode
                 & Configuration.UI_MODE_NIGHT_YES) != 0;
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 c6c64c9..bc2fbc6 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -4397,7 +4397,7 @@
         when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_FOREGROUND);
 
         // enqueue toast -> toast should still enqueue
-        ((INotificationManager)mService.mService).enqueueToast(testPackage,
+        ((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(),
                 new TestableToastCallback(), 2000, 0);
         assertEquals(1, mService.mToastQueue.size());
     }
@@ -4417,7 +4417,7 @@
         when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_LOW);
 
         // enqueue toast -> no toasts enqueued
-        ((INotificationManager)mService.mService).enqueueToast(testPackage,
+        ((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(),
                 new TestableToastCallback(), 2000, 0);
         assertEquals(0, mService.mToastQueue.size());
     }
@@ -4440,7 +4440,7 @@
         when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_GONE);
 
         // enqueue toast -> no toasts enqueued
-        ((INotificationManager)mService.mService).enqueueToast(testPackage,
+        ((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(),
                 new TestableToastCallback(), 2000, 0);
         assertEquals(0, mService.mToastQueue.size());
     }
@@ -4463,7 +4463,7 @@
         when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_GONE);
 
         // enqueue toast -> system toast can still be enqueued
-        ((INotificationManager)mService.mService).enqueueToast(testPackage,
+        ((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(),
                 new TestableToastCallback(), 2000, 0);
         assertEquals(1, mService.mToastQueue.size());
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index ed9cdf4..4f84ee1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -233,7 +233,7 @@
 
         final ActivityStack pinnedStack = display.createStack(
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        assertEquals(pinnedStack, display.getPinnedStack());
+        assertEquals(pinnedStack, display.getRootPinnedTask());
         assertEquals(pinnedStack, display.getTopStack());
 
         final ActivityStack anotherAlwaysOnTopStack = display.createStack(
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index c60ca48..ebe4ab9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -136,7 +136,7 @@
     public void testStackCleanupOnTaskRemoval() {
         mStack.removeChild(mTask, null /*reason*/);
         // Stack should be gone on task removal.
-        assertNull(mService.mRootWindowContainer.getStack(mStack.mStackId));
+        assertNull(mService.mRootWindowContainer.getStack(mStack.mTaskId));
     }
 
     @Test
@@ -1010,7 +1010,7 @@
     @Test
     public void testDestroyIfPossible_lastActivityAboveEmptyHomeStack() {
         // Empty the home stack.
-        final ActivityStack homeStack = mActivity.getDisplay().getHomeStack();
+        final ActivityStack homeStack = mActivity.getDisplay().getRootHomeTask();
         homeStack.forAllTasks((t) -> {
             homeStack.removeChild(t, "test");
         }, true /* traverseTopToBottom */, homeStack);
@@ -1036,7 +1036,7 @@
     @Test
     public void testCompleteFinishing_lastActivityAboveEmptyHomeStack() {
         // Empty the home stack.
-        final ActivityStack homeStack = mActivity.getDisplay().getHomeStack();
+        final ActivityStack homeStack = mActivity.getDisplay().getRootHomeTask();
         homeStack.forAllTasks((t) -> {
             homeStack.removeChild(t, "test");
         }, true /* traverseTopToBottom */, homeStack);
@@ -1136,7 +1136,7 @@
 
     @Test
     public void testRemoveFromHistory() {
-        final ActivityStack stack = mActivity.getActivityStack();
+        final ActivityStack stack = mActivity.getRootTask();
         final Task task = mActivity.getTask();
 
         mActivity.removeFromHistory("test");
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 9e54f40..f702d5d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -153,7 +153,7 @@
         assertThat((Object) task2.getStack()).isInstanceOf(ActivityStack.class);
         mStarter.updateBounds(task2, bounds);
 
-        verify(mService, times(1)).animateResizePinnedStack(eq(task2.getStack().mStackId),
+        verify(mService, times(1)).animateResizePinnedStack(eq(task2.getRootTaskId()),
                 eq(bounds), anyInt());
 
         // In the case of no animation, the stack and task bounds should be set immediately.
@@ -446,13 +446,13 @@
                 new ActivityBuilder(mService).setCreateTask(true).build();
         final ActivityRecord splitSecondReusableActivity =
                 new ActivityBuilder(mService).setCreateTask(true).build();
-        splitPrimaryFocusActivity.getActivityStack()
+        splitPrimaryFocusActivity.getRootTask()
                 .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-        splitSecondReusableActivity.getActivityStack()
+        splitSecondReusableActivity.getRootTask()
                 .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
 
         // Set focus back to primary.
-        splitPrimaryFocusActivity.getActivityStack().moveToFront("testSplitScreenDeliverToTop");
+        splitPrimaryFocusActivity.getRootTask().moveToFront("testSplitScreenDeliverToTop");
 
         // Start activity and delivered new intent.
         starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent);
@@ -477,18 +477,18 @@
                 new ActivityBuilder(mService).setCreateTask(true).build();
         final ActivityRecord splitPrimaryFocusActivity =
                 new ActivityBuilder(mService).setCreateTask(true).build();
-        splitPrimaryFocusActivity.getActivityStack()
+        splitPrimaryFocusActivity.getRootTask()
                 .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-        splitSecondReusableActivity.getActivityStack()
+        splitSecondReusableActivity.getRootTask()
                 .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-        splitSecondTopActivity.getActivityStack()
+        splitSecondTopActivity.getRootTask()
                 .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
 
         // Make it on top of split-screen-secondary.
-        splitSecondTopActivity.getActivityStack().moveToFront("testSplitScreenTaskToFront");
+        splitSecondTopActivity.getRootTask().moveToFront("testSplitScreenTaskToFront");
 
         // Let primary stack has focus.
-        splitPrimaryFocusActivity.getActivityStack().moveToFront("testSplitScreenTaskToFront");
+        splitPrimaryFocusActivity.getRootTask().moveToFront("testSplitScreenTaskToFront");
 
         // Start activity and delivered new intent.
         starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent);
@@ -732,13 +732,13 @@
         final ActivityRecord activity = new ActivityBuilder(mService)
                 .setCreateTask(true).build();
         new ActivityBuilder(mService)
-                .setStack(activity.getActivityStack())
+                .setStack(activity.getRootTask())
                 .setCreateTask(true).build();
 
         // Create a top finishing activity.
         final ActivityRecord finishingTopActivity = new ActivityBuilder(mService)
                 .setCreateTask(true).build();
-        finishingTopActivity.getActivityStack().moveToFront("finishingTopActivity");
+        finishingTopActivity.getRootTask().moveToFront("finishingTopActivity");
 
         assertEquals(finishingTopActivity, mRootWindowContainer.topRunningActivity());
         finishingTopActivity.finishing = true;
@@ -997,8 +997,8 @@
         assertThat(outActivity[0].inSplitScreenWindowingMode()).isFalse();
 
         // Move activity to split-screen-primary stack and make sure it has the focus.
-        top.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-        top.getActivityStack().moveToFront("testWindowingModeOptionsLaunchAdjacent");
+        top.getRootTask().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        top.getRootTask().moveToFront("testWindowingModeOptionsLaunchAdjacent");
 
         // Activity must landed on split-screen-secondary when launch adjacent.
         starter.setActivityOptions(options.toBundle())
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index cf1f0a8..8b3a6cf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -284,7 +284,7 @@
 
         doNothing().when(mService).moveStackToDisplay(anyInt(), anyInt());
         mController.layoutTask(task, null /* windowLayout */);
-        verify(mService, times(1)).moveStackToDisplay(eq(task.getStackId()),
+        verify(mService, times(1)).moveStackToDisplay(eq(task.getRootTaskId()),
                 eq(params.mPreferredDisplayId));
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 79f808e..b5e7dd5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -303,7 +303,7 @@
         // other task
         Task task1 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
-                .setStack(mDisplay.getHomeStack()).build();
+                .setStack(mDisplay.getRootHomeTask()).build();
         Task task2 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
                 .setStack(mStack).build();
@@ -792,7 +792,7 @@
     public void testBackStackTasks_expectNoTrim() {
         mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
 
-        final ActivityStack homeStack = mDisplay.getHomeStack();
+        final ActivityStack homeStack = mDisplay.getRootHomeTask();
         final ActivityStack aboveHomeStack = mDisplay.createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
@@ -812,7 +812,7 @@
 
         final ActivityStack behindHomeStack = mDisplay.createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ActivityStack homeStack = mDisplay.getHomeStack();
+        final ActivityStack homeStack = mDisplay.getRootHomeTask();
         final ActivityStack aboveHomeStack = mDisplay.createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
@@ -832,7 +832,7 @@
     public void testOtherDisplayTasks_expectNoTrim() {
         mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
 
-        final ActivityStack homeStack = mDisplay.getHomeStack();
+        final ActivityStack homeStack = mDisplay.getRootHomeTask();
         final DisplayContent otherDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
         final ActivityStack otherDisplayStack = otherDisplay.createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 7e42c62..b78107e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -97,7 +97,7 @@
                 .setStack(recentsStack)
                 .build();
         ActivityRecord topActivity = new ActivityBuilder(mService).setCreateTask(true).build();
-        topActivity.getActivityStack().moveToFront("testRecentsActivityVisiblility");
+        topActivity.getRootTask().moveToFront("testRecentsActivityVisiblility");
 
         doCallRealMethod().when(mRootWindowContainer).ensureActivitiesVisible(
                 any() /* starting */, anyInt() /* configChanges */,
@@ -187,7 +187,7 @@
         recentActivity.app = null;
 
         // Start an activity on top.
-        new ActivityBuilder(mService).setCreateTask(true).build().getActivityStack().moveToFront(
+        new ActivityBuilder(mService).setCreateTask(true).build().getRootTask().moveToFront(
                 "testRestartRecentsActivity");
 
         doCallRealMethod().when(mRootWindowContainer).ensureActivitiesVisible(
@@ -209,7 +209,7 @@
     public void testSetLaunchTaskBehindOfTargetActivity() {
         DisplayContent display = mRootWindowContainer.getDefaultDisplay();
         display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class);
-        ActivityStack homeStack = display.getHomeStack();
+        ActivityStack homeStack = display.getRootHomeTask();
         // Assume the home activity support recents.
         ActivityRecord targetActivity = homeStack.getTopNonFinishingActivity();
         if (targetActivity == null) {
@@ -229,7 +229,7 @@
         new ActivityBuilder(mService)
                 .setCreateTask(true)
                 .build()
-                .getActivityStack()
+                .getRootTask()
                 .moveToFront("Activity start");
 
         // Start the recents animation.
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index ea8d082..79db927 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -131,7 +131,7 @@
                 0f /*aspectRatio*/, "initialMove");
 
         final DisplayContent display = mFullscreenStack.getDisplay();
-        ActivityStack pinnedStack = display.getPinnedStack();
+        ActivityStack pinnedStack = display.getRootPinnedTask();
         // Ensure a task has moved over.
         ensureStackPlacement(pinnedStack, firstActivity);
         ensureStackPlacement(mFullscreenStack, secondActivity);
@@ -141,7 +141,7 @@
                 0f /*aspectRatio*/, "secondMove");
 
         // Need to get stacks again as a new instance might have been created.
-        pinnedStack = display.getPinnedStack();
+        pinnedStack = display.getRootPinnedTask();
         mFullscreenStack = display.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
         // Ensure stacks have swapped tasks.
         ensureStackPlacement(pinnedStack, secondActivity);
@@ -417,7 +417,7 @@
     @Test
     public void testResumeFocusedStacksStartsHomeActivity_NoActivities() {
         mFullscreenStack.removeIfPossible();
-        mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY).getHomeStack()
+        mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY).getRootHomeTask()
                 .removeIfPossible();
         mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY)
                 .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
@@ -440,7 +440,7 @@
     @Test
     public void testResumeFocusedStacksStartsHomeActivity_ActivityOnSecondaryScreen() {
         mFullscreenStack.removeIfPossible();
-        mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY).getHomeStack()
+        mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY).getRootHomeTask()
                 .removeIfPossible();
         mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY)
                 .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
@@ -638,7 +638,7 @@
         final ActivityRecord resolverActivity = mRootWindowContainer.topRunningActivity();
 
         assertEquals(info, resolverActivity.info);
-        assertEquals(ACTIVITY_TYPE_STANDARD, resolverActivity.getActivityStack().getActivityType());
+        assertEquals(ACTIVITY_TYPE_STANDARD, resolverActivity.getRootTask().getActivityType());
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskContainersTests.java
similarity index 98%
rename from services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskContainersTests.java
index 6ad9f74..f228397 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskContainersTests.java
@@ -46,7 +46,7 @@
 @SmallTest
 @Presubmit
 @RunWith(WindowTestRunner.class)
-public class TaskStackContainersTests extends WindowTestsBase {
+public class TaskContainersTests extends WindowTestsBase {
 
     private ActivityStack mPinnedStack;
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
index 8970571..7aaf3fb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
@@ -85,7 +85,7 @@
 
         mPositioner.register(mDisplayContent, win);
 
-        win.getStack().setWindowingMode(WINDOWING_MODE_FREEFORM);
+        win.getRootTask().setWindowingMode(WINDOWING_MODE_FREEFORM);
     }
 
     @After
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 6d0b54f..5cf9c44 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -278,7 +278,7 @@
         spyOn(stack);
         doReturn(true).when(controller).isMinimizedDock();
         doReturn(true).when(controller).isHomeStackResizable();
-        doReturn(stack).when(appWindow).getStack();
+        doReturn(stack).when(appWindow).getRootTask();
 
         // Make sure canBeImeTarget is false due to shouldIgnoreInput is true;
         assertFalse(appWindow.canBeImeTarget());
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c9d5006..2c21a60 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -45,7 +45,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.net.ConnectivityManager;
 import android.net.NetworkStats;
@@ -11103,8 +11102,8 @@
      */
     public boolean isDataCapable() {
         if (mContext == null) return true;
-        return mContext.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_TELEPHONY_DATA);
+        return mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_mobile_data_capable);
     }
 
     /**
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 6e630e3..7896320 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -305,42 +305,44 @@
     private static final Map<Integer, String> MVNO_TYPE_INT_MAP;
 
     static {
-        APN_TYPE_STRING_MAP = new ArrayMap<String, Integer>();
-        APN_TYPE_STRING_MAP.put("*", TYPE_ALL);
-        APN_TYPE_STRING_MAP.put("default", TYPE_DEFAULT);
-        APN_TYPE_STRING_MAP.put("mms", TYPE_MMS);
-        APN_TYPE_STRING_MAP.put("supl", TYPE_SUPL);
-        APN_TYPE_STRING_MAP.put("dun", TYPE_DUN);
-        APN_TYPE_STRING_MAP.put("hipri", TYPE_HIPRI);
-        APN_TYPE_STRING_MAP.put("fota", TYPE_FOTA);
-        APN_TYPE_STRING_MAP.put("ims", TYPE_IMS);
-        APN_TYPE_STRING_MAP.put("cbs", TYPE_CBS);
-        APN_TYPE_STRING_MAP.put("ia", TYPE_IA);
-        APN_TYPE_STRING_MAP.put("emergency", TYPE_EMERGENCY);
-        APN_TYPE_STRING_MAP.put("mcx", TYPE_MCX);
-        APN_TYPE_STRING_MAP.put("xcap", TYPE_XCAP);
-        APN_TYPE_INT_MAP = new ArrayMap<Integer, String>();
-        APN_TYPE_INT_MAP.put(TYPE_DEFAULT, "default");
-        APN_TYPE_INT_MAP.put(TYPE_MMS, "mms");
-        APN_TYPE_INT_MAP.put(TYPE_SUPL, "supl");
-        APN_TYPE_INT_MAP.put(TYPE_DUN, "dun");
-        APN_TYPE_INT_MAP.put(TYPE_HIPRI, "hipri");
-        APN_TYPE_INT_MAP.put(TYPE_FOTA, "fota");
-        APN_TYPE_INT_MAP.put(TYPE_IMS, "ims");
-        APN_TYPE_INT_MAP.put(TYPE_CBS, "cbs");
-        APN_TYPE_INT_MAP.put(TYPE_IA, "ia");
-        APN_TYPE_INT_MAP.put(TYPE_EMERGENCY, "emergency");
-        APN_TYPE_INT_MAP.put(TYPE_MCX, "mcx");
-        APN_TYPE_INT_MAP.put(TYPE_XCAP, "xcap");
+        APN_TYPE_STRING_MAP = new ArrayMap<>();
+        APN_TYPE_STRING_MAP.put(TYPE_ALL_STRING, TYPE_ALL);
+        APN_TYPE_STRING_MAP.put(TYPE_DEFAULT_STRING, TYPE_DEFAULT);
+        APN_TYPE_STRING_MAP.put(TYPE_MMS_STRING, TYPE_MMS);
+        APN_TYPE_STRING_MAP.put(TYPE_SUPL_STRING, TYPE_SUPL);
+        APN_TYPE_STRING_MAP.put(TYPE_DUN_STRING, TYPE_DUN);
+        APN_TYPE_STRING_MAP.put(TYPE_HIPRI_STRING, TYPE_HIPRI);
+        APN_TYPE_STRING_MAP.put(TYPE_FOTA_STRING, TYPE_FOTA);
+        APN_TYPE_STRING_MAP.put(TYPE_IMS_STRING, TYPE_IMS);
+        APN_TYPE_STRING_MAP.put(TYPE_CBS_STRING, TYPE_CBS);
+        APN_TYPE_STRING_MAP.put(TYPE_IA_STRING, TYPE_IA);
+        APN_TYPE_STRING_MAP.put(TYPE_EMERGENCY_STRING, TYPE_EMERGENCY);
+        APN_TYPE_STRING_MAP.put(TYPE_MCX_STRING, TYPE_MCX);
+        APN_TYPE_STRING_MAP.put(TYPE_XCAP_STRING, TYPE_XCAP);
 
-        PROTOCOL_STRING_MAP = new ArrayMap<String, Integer>();
+        APN_TYPE_INT_MAP = new ArrayMap<>();
+        APN_TYPE_INT_MAP.put(TYPE_DEFAULT, TYPE_DEFAULT_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_MMS, TYPE_MMS_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_SUPL, TYPE_SUPL_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_DUN, TYPE_DUN_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_HIPRI, TYPE_HIPRI_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_FOTA, TYPE_FOTA_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_IMS, TYPE_IMS_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_CBS, TYPE_CBS_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_IA, TYPE_IA_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_EMERGENCY, TYPE_EMERGENCY_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_MCX, TYPE_MCX_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_XCAP, TYPE_XCAP_STRING);
+
+        PROTOCOL_STRING_MAP = new ArrayMap<>();
         PROTOCOL_STRING_MAP.put("IP", PROTOCOL_IP);
         PROTOCOL_STRING_MAP.put("IPV6", PROTOCOL_IPV6);
         PROTOCOL_STRING_MAP.put("IPV4V6", PROTOCOL_IPV4V6);
         PROTOCOL_STRING_MAP.put("PPP", PROTOCOL_PPP);
         PROTOCOL_STRING_MAP.put("NON-IP", PROTOCOL_NON_IP);
         PROTOCOL_STRING_MAP.put("UNSTRUCTURED", PROTOCOL_UNSTRUCTURED);
-        PROTOCOL_INT_MAP = new ArrayMap<Integer, String>();
+
+        PROTOCOL_INT_MAP = new ArrayMap<>();
         PROTOCOL_INT_MAP.put(PROTOCOL_IP, "IP");
         PROTOCOL_INT_MAP.put(PROTOCOL_IPV6, "IPV6");
         PROTOCOL_INT_MAP.put(PROTOCOL_IPV4V6, "IPV4V6");
@@ -348,12 +350,13 @@
         PROTOCOL_INT_MAP.put(PROTOCOL_NON_IP, "NON-IP");
         PROTOCOL_INT_MAP.put(PROTOCOL_UNSTRUCTURED, "UNSTRUCTURED");
 
-        MVNO_TYPE_STRING_MAP = new ArrayMap<String, Integer>();
+        MVNO_TYPE_STRING_MAP = new ArrayMap<>();
         MVNO_TYPE_STRING_MAP.put("spn", MVNO_TYPE_SPN);
         MVNO_TYPE_STRING_MAP.put("imsi", MVNO_TYPE_IMSI);
         MVNO_TYPE_STRING_MAP.put("gid", MVNO_TYPE_GID);
         MVNO_TYPE_STRING_MAP.put("iccid", MVNO_TYPE_ICCID);
-        MVNO_TYPE_INT_MAP = new ArrayMap<Integer, String>();
+
+        MVNO_TYPE_INT_MAP = new ArrayMap<>();
         MVNO_TYPE_INT_MAP.put(MVNO_TYPE_SPN, "spn");
         MVNO_TYPE_INT_MAP.put(MVNO_TYPE_IMSI, "imsi");
         MVNO_TYPE_INT_MAP.put(MVNO_TYPE_GID, "gid");
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index c02f8c3..c8fd243 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -212,7 +212,6 @@
      */
     public static final int SECURITY_TYPE_OPEN = 0;
 
-
     /**
      * The definition of security type WPA2-PSK.
      */
@@ -408,7 +407,11 @@
     /**
      * Get security type params which depends on which security passphrase to set.
      *
-     * @return One of the security types from {@link SecurityType}.
+     * @return One of:
+     * {@link #SECURITY_TYPE_OPEN},
+     * {@link #SECURITY_TYPE_WPA2_PSK},
+     * {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION},
+     * {@link #SECURITY_TYPE_WPA3_SAE}
      */
     public @SecurityType int getSecurityType() {
         return mSecurityType;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index c35303d..208ce9c 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -201,6 +201,13 @@
      */
     public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_NOT_ALLOWED = 6;
 
+    /**
+     * Reason code if one or more of the network suggestions added is invalid.
+     *
+     * Please user {@link WifiNetworkSuggestion.Builder} to create network suggestions.
+     */
+    public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_INVALID = 7;
+
     /** @hide */
     @IntDef(prefix = { "STATUS_NETWORK_SUGGESTIONS_" }, value = {
             STATUS_NETWORK_SUGGESTIONS_SUCCESS,
@@ -210,6 +217,7 @@
             STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP,
             STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID,
             STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_NOT_ALLOWED,
+            STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_INVALID,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface NetworkSuggestionsStatusCode {}