Merge "Build statsd framework jar against module_current"
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index d4ceabd..7b3764a 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -334,7 +334,8 @@
                 throw new SecurityException("Caller not allowed to access " + blobHandle
                         + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
             }
-            if (leaseExpiryTimeMillis != 0 && leaseExpiryTimeMillis > blobHandle.expiryTimeMillis) {
+            if (leaseExpiryTimeMillis != 0 && blobHandle.expiryTimeMillis != 0
+                    && leaseExpiryTimeMillis > blobHandle.expiryTimeMillis) {
                 throw new IllegalArgumentException(
                         "Lease expiry cannot be later than blobs expiry time");
             }
diff --git a/api/current.txt b/api/current.txt
index ac42e66..a0b38cd 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -36633,6 +36633,7 @@
     method public void addThermalStatusListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.PowerManager.OnThermalStatusChangedListener);
     method public int getCurrentThermalStatus();
     method public int getLocationPowerSaveMode();
+    method public float getThermalHeadroom(@IntRange(from=0, to=60) int);
     method public boolean isDeviceIdleMode();
     method public boolean isIgnoringBatteryOptimizations(String);
     method public boolean isInteractive();
@@ -40585,13 +40586,13 @@
     field public static final String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
     field public static final String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS = "wifi_mobile_data_transition_wakelock_timeout_ms";
     field @Deprecated public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = "wifi_networks_available_notification_on";
-    field public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = "wifi_networks_available_repeat_delay";
-    field public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
+    field @Deprecated public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = "wifi_networks_available_repeat_delay";
+    field @Deprecated public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
     field public static final String WIFI_ON = "wifi_on";
-    field public static final String WIFI_SLEEP_POLICY = "wifi_sleep_policy";
-    field public static final int WIFI_SLEEP_POLICY_DEFAULT = 0; // 0x0
-    field public static final int WIFI_SLEEP_POLICY_NEVER = 2; // 0x2
-    field public static final int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1; // 0x1
+    field @Deprecated public static final String WIFI_SLEEP_POLICY = "wifi_sleep_policy";
+    field @Deprecated public static final int WIFI_SLEEP_POLICY_DEFAULT = 0; // 0x0
+    field @Deprecated public static final int WIFI_SLEEP_POLICY_NEVER = 2; // 0x2
+    field @Deprecated public static final int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1; // 0x1
     field public static final String WIFI_WATCHDOG_ON = "wifi_watchdog_on";
     field public static final String WINDOW_ANIMATION_SCALE = "window_animation_scale";
   }
@@ -53891,6 +53892,7 @@
     method public boolean requestRectangleOnScreen(android.graphics.Rect);
     method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
     method public final void requestUnbufferedDispatch(android.view.MotionEvent);
+    method public final void requestUnbufferedDispatch(int);
     method @NonNull public final <T extends android.view.View> T requireViewById(@IdRes int);
     method public void resetPivot();
     method public static int resolveSize(int, int);
@@ -55604,6 +55606,7 @@
     method public CharSequence getContentDescription();
     method public int getDrawingOrder();
     method public CharSequence getError();
+    method @Nullable public android.view.accessibility.AccessibilityNodeInfo.ExtraRenderingInfo getExtraRenderingInfo();
     method public android.os.Bundle getExtras();
     method public CharSequence getHintText();
     method public int getInputType();
@@ -55756,6 +55759,7 @@
     field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
     field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
     field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo> CREATOR;
+    field public static final String EXTRA_DATA_RENDERING_INFO_KEY = "android.view.accessibility.extra.DATA_RENDERING_INFO_KEY";
     field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
     field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
     field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
@@ -55843,6 +55847,12 @@
     method public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean, boolean);
   }
 
+  public static final class AccessibilityNodeInfo.ExtraRenderingInfo {
+    method @Nullable public android.util.Size getLayoutParams();
+    method public float getTextSizeInPx();
+    method public int getTextSizeUnit();
+  }
+
   public static final class AccessibilityNodeInfo.RangeInfo {
     ctor public AccessibilityNodeInfo.RangeInfo(int, float, float, float);
     method public float getCurrent();
@@ -60825,6 +60835,7 @@
     method @Nullable public android.graphics.drawable.Drawable getTextSelectHandleLeft();
     method @Nullable public android.graphics.drawable.Drawable getTextSelectHandleRight();
     method @android.view.ViewDebug.ExportedProperty(category="text") public float getTextSize();
+    method public int getTextSizeUnit();
     method public int getTotalPaddingBottom();
     method public int getTotalPaddingEnd();
     method public int getTotalPaddingLeft();
diff --git a/api/system-current.txt b/api/system-current.txt
index f974380..a7dbf38 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7536,6 +7536,7 @@
     method public int getChannel();
     method public int getMaxNumberOfClients();
     method public int getShutdownTimeoutMillis();
+    method public boolean isAutoShutdownEnabled();
     method public boolean isClientControlByUserEnabled();
     method @Nullable public android.net.wifi.WifiConfiguration toWifiConfiguration();
     field public static final int BAND_2GHZ = 1; // 0x1
@@ -7549,14 +7550,15 @@
     ctor public SoftApConfiguration.Builder(@NonNull android.net.wifi.SoftApConfiguration);
     method @NonNull public android.net.wifi.SoftApConfiguration build();
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder enableClientControlByUser(boolean);
+    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setAutoShutdownEnabled(boolean);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBand(int);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBssid(@Nullable android.net.MacAddress);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannel(int, int);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setClientList(@NonNull java.util.List<android.net.MacAddress>, @NonNull java.util.List<android.net.MacAddress>);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setHiddenSsid(boolean);
-    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(int);
+    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(@IntRange(from=0) int);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setPassphrase(@Nullable String, int);
-    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setShutdownTimeoutMillis(int);
+    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setShutdownTimeoutMillis(@IntRange(from=0) int);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String);
   }
 
@@ -7750,6 +7752,7 @@
     method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMeteredOverridePasspoint(@NonNull String, int);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setScanAlwaysAvailable(boolean);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setScanThrottleEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean setSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setVerboseLoggingEnabled(boolean);
@@ -7897,6 +7900,7 @@
 
   public final class WifiOemMigrationHook {
     method @Nullable public static android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData loadFromConfigStore();
+    method @NonNull public static android.net.wifi.WifiOemMigrationHook.SettingsMigrationData loadFromSettings(@NonNull android.content.Context);
   }
 
   public static final class WifiOemMigrationHook.ConfigStoreMigrationData implements android.os.Parcelable {
@@ -7914,6 +7918,31 @@
     method @NonNull public android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData.Builder setUserSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
   }
 
+  public static final class WifiOemMigrationHook.SettingsMigrationData implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public String getP2pDeviceName();
+    method public boolean isP2pFactoryResetPending();
+    method public boolean isScanAlwaysAvailable();
+    method public boolean isScanThrottleEnabled();
+    method public boolean isSoftApTimeoutEnabled();
+    method public boolean isVerboseLoggingEnabled();
+    method public boolean isWakeUpEnabled();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiOemMigrationHook.SettingsMigrationData> CREATOR;
+  }
+
+  public static final class WifiOemMigrationHook.SettingsMigrationData.Builder {
+    ctor public WifiOemMigrationHook.SettingsMigrationData.Builder();
+    method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData build();
+    method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setP2pDeviceName(@Nullable String);
+    method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setP2pFactoryResetPending(boolean);
+    method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setScanAlwaysAvailable(boolean);
+    method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setScanThrottleEnabled(boolean);
+    method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setSoftApTimeoutEnabled(boolean);
+    method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setVerboseLoggingEnabled(boolean);
+    method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setWakeUpEnabled(boolean);
+  }
+
   public class WifiScanner {
     method @Deprecated public void configureWifiChange(int, int, int, int, int, android.net.wifi.WifiScanner.BssidInfo[]);
     method @Deprecated public void configureWifiChange(android.net.wifi.WifiScanner.WifiChangeSettings);
@@ -9720,18 +9749,11 @@
     field public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis";
     field public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
     field public static final String REQUIRE_PASSWORD_TO_DECRYPT = "require_password_to_decrypt";
-    field public static final String SOFT_AP_TIMEOUT_ENABLED = "soft_ap_timeout_enabled";
     field public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled";
     field public static final String TETHER_SUPPORTED = "tether_supported";
     field public static final String THEATER_MODE_ON = "theater_mode_on";
     field public static final String WEBVIEW_MULTIPROCESS = "webview_multiprocess";
     field public static final String WIFI_BADGING_THRESHOLDS = "wifi_badging_thresholds";
-    field public static final String WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name";
-    field public static final String WIFI_P2P_PENDING_FACTORY_RESET = "wifi_p2p_pending_factory_reset";
-    field public static final String WIFI_SCAN_ALWAYS_AVAILABLE = "wifi_scan_always_enabled";
-    field public static final String WIFI_SCAN_THROTTLE_ENABLED = "wifi_scan_throttle_enabled";
-    field public static final String WIFI_SCORE_PARAMS = "wifi_score_params";
-    field public static final String WIFI_VERBOSE_LOGGING_ENABLED = "wifi_verbose_logging_enabled";
     field @Deprecated public static final String WIFI_WAKEUP_ENABLED = "wifi_wakeup_enabled";
   }
 
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index bf6afe7..a4e8fdc 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -8231,14 +8231,6 @@
 }
 
 /**
- * HWUI renders pipeline type: GL (0) or Vulkan (1).
- */
-enum PipelineType {
-    GL = 0;
-    VULKAN = 1;
-}
-
-/**
  * HWUI stats for a given app.
  */
 message GraphicsStats {
@@ -8251,9 +8243,16 @@
     // The start & end timestamps in UTC as
     // milliseconds since January 1, 1970
     // Compatible with java.util.Date#setTime()
-    optional int64 stats_start = 3;
+    optional int64 start_millis = 3;
 
-    optional int64 stats_end = 4;
+    optional int64 end_millis = 4;
+
+    // HWUI renders pipeline type: GL (1) or Vulkan (2).
+    enum PipelineType {
+        UNKNOWN = 0;
+        GL = 1;
+        VULKAN = 2;
+    }
 
     // HWUI renders pipeline type: GL or Vulkan.
     optional PipelineType pipeline = 5;
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 83d609c..0a138cf 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -960,7 +960,7 @@
             return false;
         }
         List<GestureDescription.GestureStep> steps =
-                MotionEventGenerator.getGestureStepsFromGestureDescription(gesture, 100);
+                MotionEventGenerator.getGestureStepsFromGestureDescription(gesture, 16);
         try {
             synchronized (mLock) {
                 mGestureStatusCallbackSequence++;
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 526c0b3..948546b 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -88,6 +88,7 @@
     void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList);
     void createNotificationChannels(String pkg, in ParceledListSlice channelsList);
     void createNotificationChannelsForPackage(String pkg, int uid, in ParceledListSlice channelsList);
+    ParceledListSlice getConversations(boolean onlyImportant);
     ParceledListSlice getConversationsForPackage(String pkg, int uid);
     ParceledListSlice getNotificationChannelGroupsForPackage(String pkg, int uid, boolean includeDeleted);
     NotificationChannelGroup getNotificationChannelGroupForPackage(String groupId, String pkg, int uid);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 655dd9b..f1559f7 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -148,6 +148,7 @@
 import android.os.IPowerManager;
 import android.os.IRecoverySystem;
 import android.os.ISystemUpdateManager;
+import android.os.IThermalService;
 import android.os.IUserManager;
 import android.os.IncidentManager;
 import android.os.PowerManager;
@@ -576,10 +577,12 @@
                 new CachedServiceFetcher<PowerManager>() {
             @Override
             public PowerManager createService(ContextImpl ctx) throws ServiceNotFoundException {
-                IBinder b = ServiceManager.getServiceOrThrow(Context.POWER_SERVICE);
-                IPowerManager service = IPowerManager.Stub.asInterface(b);
-                return new PowerManager(ctx.getOuterContext(),
-                        service, ctx.mMainThread.getHandler());
+                IBinder powerBinder = ServiceManager.getServiceOrThrow(Context.POWER_SERVICE);
+                IPowerManager powerService = IPowerManager.Stub.asInterface(powerBinder);
+                IBinder thermalBinder = ServiceManager.getServiceOrThrow(Context.THERMAL_SERVICE);
+                IThermalService thermalService = IThermalService.Stub.asInterface(thermalBinder);
+                return new PowerManager(ctx.getOuterContext(), powerService, thermalService,
+                        ctx.mMainThread.getHandler());
             }});
 
         registerService(Context.RECOVERY_SERVICE, RecoverySystem.class,
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 5b60b85..3e1a480 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -25,6 +25,7 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
+import android.app.PropertyInvalidatedCache;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.Handler;
@@ -1299,6 +1300,31 @@
         return false;
     }
 
+    private static final String BLUETOOTH_BONDING_CACHE_PROPERTY =
+            "cache_key.bluetooth.get_bond_state";
+    private final PropertyInvalidatedCache<BluetoothDevice, Integer> mBluetoothBondCache =
+            new PropertyInvalidatedCache<BluetoothDevice, Integer>(
+                8, BLUETOOTH_BONDING_CACHE_PROPERTY) {
+                @Override
+                protected Integer recompute(BluetoothDevice query) {
+                    try {
+                        return sService.getBondState(query);
+                    } catch (RemoteException e) {
+                        throw e.rethrowAsRuntimeException();
+                    }
+                }
+            };
+
+    /** @hide */
+    public void disableBluetoothGetBondStateCache() {
+        mBluetoothBondCache.disableLocal();
+    }
+
+    /** @hide */
+    public static void invalidateBluetoothGetBondStateCache() {
+        PropertyInvalidatedCache.invalidateCache(BLUETOOTH_BONDING_CACHE_PROPERTY);
+    }
+
     /**
      * Get the bond state of the remote device.
      * <p>Possible values for the bond state are:
@@ -1316,9 +1342,13 @@
             return BOND_NONE;
         }
         try {
-            return service.getBondState(this);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
+            return mBluetoothBondCache.query(this);
+        } catch (RuntimeException e) {
+            if (e.getCause() instanceof RemoteException) {
+                Log.e(TAG, "", e);
+            } else {
+                throw e;
+            }
         }
         return BOND_NONE;
     }
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index a69905e..3a93421 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -22,6 +22,7 @@
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.LocusId;
 import android.content.pm.LauncherApps.ShortcutQuery;
@@ -92,4 +93,11 @@
     public abstract void uncacheShortcuts(int launcherUserId,
             @NonNull String callingPackage, @NonNull String packageName,
             @NonNull List<String> shortcutIds, int userId);
+
+    /**
+     * Retrieves all of the direct share targets that match the given IntentFilter for the specified
+     * user.
+     */
+    public abstract List<ShortcutManager.ShareShortcutInfo> getShareTargets(
+            @NonNull String callingPackage, @NonNull IntentFilter intentFilter, int userId);
 }
diff --git a/core/java/android/hardware/display/DeviceProductInfo.java b/core/java/android/hardware/display/DeviceProductInfo.java
new file mode 100644
index 0000000..6ad7fae
--- /dev/null
+++ b/core/java/android/hardware/display/DeviceProductInfo.java
@@ -0,0 +1,224 @@
+/*
+ * 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 android.hardware.display;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Product-specific information about the display or the directly connected device on the
+ * display chain. For example, if the display is transitively connected, this field may contain
+ * product information about the intermediate device.
+ * @hide
+ */
+public final class DeviceProductInfo implements Parcelable {
+    final private String mName;
+    final private String mManufacturerPnpId;
+    final private String mProductId;
+    final private Integer mModelYear;
+    final private ManufactureDate mManufactureDate;
+
+    public DeviceProductInfo(
+            String name,
+            String manufacturerPnpId,
+            String productCode,
+            Integer modelYear,
+            ManufactureDate manufactureDate) {
+        this.mName = name;
+        this.mManufacturerPnpId = manufacturerPnpId;
+        this.mProductId = productCode;
+        this.mModelYear = modelYear;
+        this.mManufactureDate = manufactureDate;
+    }
+
+    private DeviceProductInfo(Parcel in) {
+        mName = in.readString();
+        mManufacturerPnpId = in.readString();
+        mProductId = (String) in.readValue(null);
+        mModelYear = (Integer) in.readValue(null);
+        mManufactureDate = (ManufactureDate) in.readValue(null);
+    }
+
+    /**
+     * @return Display name.
+     */
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * @return Manufacturer Plug and Play ID.
+     */
+    public String getManufacturerPnpId() {
+        return mManufacturerPnpId;
+    }
+
+    /**
+     * @return Manufacturer product ID.
+     */
+    public String getProductId() {
+        return mProductId;
+    }
+
+    /**
+     * @return Model year of the device. Typically exactly one of model year or
+     *      manufacture date will be present.
+     */
+    public Integer getModelYear() {
+        return mModelYear;
+    }
+
+    /**
+     * @return Manufacture date. Typically exactly one of model year or manufacture
+     * date will be present.
+     */
+    public ManufactureDate getManufactureDate() {
+        return mManufactureDate;
+    }
+
+    @Override
+    public String toString() {
+        return "DeviceProductInfo{"
+                + "name="
+                + mName
+                + ", manufacturerPnpId="
+                + mManufacturerPnpId
+                + ", productId="
+                + mProductId
+                + ", modelYear="
+                + mModelYear
+                + ", manufactureDate="
+                + mManufactureDate
+                + '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        DeviceProductInfo that = (DeviceProductInfo) o;
+        return Objects.equals(mName, that.mName)
+                && Objects.equals(mManufacturerPnpId, that.mManufacturerPnpId)
+                && Objects.equals(mProductId, that.mProductId)
+                && Objects.equals(mModelYear, that.mModelYear)
+                && Objects.equals(mManufactureDate, that.mManufactureDate);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mName, mManufacturerPnpId, mProductId, mModelYear, mManufactureDate);
+    }
+
+    public static final Creator<DeviceProductInfo> CREATOR =
+            new Creator<DeviceProductInfo>() {
+                @Override
+                public DeviceProductInfo createFromParcel(Parcel in) {
+                    return new DeviceProductInfo(in);
+                }
+
+                @Override
+                public DeviceProductInfo[] newArray(int size) {
+                    return new DeviceProductInfo[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mName);
+        dest.writeString(mManufacturerPnpId);
+        dest.writeValue(mProductId);
+        dest.writeValue(mModelYear);
+        dest.writeValue(mManufactureDate);
+    }
+
+    /**
+     * Stores information about the date of manufacture.
+     *
+     * @hide
+     */
+    public static class ManufactureDate implements Parcelable {
+        final private Integer mWeek;
+        final private Integer mYear;
+
+        public ManufactureDate(Integer week, Integer year) {
+            mWeek = week;
+            mYear = year;
+        }
+
+        protected ManufactureDate(Parcel in) {
+            mWeek = (Integer) in.readValue(null);
+            mYear = (Integer) in.readValue(null);
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeValue(mWeek);
+            dest.writeValue(mYear);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final Creator<ManufactureDate> CREATOR =
+                new Creator<ManufactureDate>() {
+                    @Override
+                    public ManufactureDate createFromParcel(Parcel in) {
+                        return new ManufactureDate(in);
+                    }
+
+                    @Override
+                    public ManufactureDate[] newArray(int size) {
+                        return new ManufactureDate[size];
+                    }
+                };
+
+        public int getYear() {
+            return mYear;
+        }
+
+        public int getWeek() {
+            return mWeek;
+        }
+
+        @Override
+        public String toString() {
+            return "ManufactureDate{week=" + mWeek + ", year=" + mYear + '}';
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            ManufactureDate that = (ManufactureDate) o;
+            return Objects.equals(mWeek, that.mWeek) && Objects.equals(mYear, that.mYear);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mWeek, mYear);
+        }
+    }
+}
diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl
index 8c98960..ad00233 100644
--- a/core/java/android/os/IThermalService.aidl
+++ b/core/java/android/os/IThermalService.aidl
@@ -103,4 +103,11 @@
       * {@hide}
       */
     List<CoolingDevice> getCurrentCoolingDevicesWithType(in int type);
+
+    /**
+     * @param forecastSeconds how many seconds ahead to forecast the provided headroom
+     * @return forecasted thermal headroom, normalized such that 1.0 indicates that throttling will
+     *     occur; returns NaN if the headroom or forecast is unavailable
+     */
+    float getThermalHeadroom(int forecastSeconds);
 }
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index a8fa6db..199b5d5 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -18,7 +18,9 @@
 
 import android.Manifest.permission;
 import android.annotation.CallbackExecutor;
+import android.annotation.CurrentTimeMillisLong;
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -39,6 +41,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicLong;
 
 /**
  * This class gives you control of the power state of the device.
@@ -916,20 +919,22 @@
     final IPowerManager mService;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     final Handler mHandler;
+    final IThermalService mThermalService;
 
     /** We lazily initialize it.*/
     private DeviceIdleManager mDeviceIdleManager;
 
-    IThermalService mThermalService;
     private final ArrayMap<OnThermalStatusChangedListener, IThermalStatusListener>
             mListenerMap = new ArrayMap<>();
 
     /**
      * {@hide}
      */
-    public PowerManager(Context context, IPowerManager service, Handler handler) {
+    public PowerManager(Context context, IPowerManager service, IThermalService thermalService,
+            Handler handler) {
         mContext = context;
         mService = service;
+        mThermalService = thermalService;
         mHandler = handler;
     }
 
@@ -1877,18 +1882,11 @@
      * thermal throttling.
      */
     public @ThermalStatus int getCurrentThermalStatus() {
-        synchronized (this) {
-            if (mThermalService == null) {
-                mThermalService = IThermalService.Stub.asInterface(
-                        ServiceManager.getService(Context.THERMAL_SERVICE));
-            }
-            try {
-                return mThermalService.getCurrentThermalStatus();
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
+        try {
+            return mThermalService.getCurrentThermalStatus();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
-
     }
 
     /**
@@ -1915,13 +1913,7 @@
      */
     public void addThermalStatusListener(@NonNull OnThermalStatusChangedListener listener) {
         Preconditions.checkNotNull(listener, "listener cannot be null");
-        synchronized (this) {
-            if (mThermalService == null) {
-                mThermalService = IThermalService.Stub.asInterface(
-                        ServiceManager.getService(Context.THERMAL_SERVICE));
-            }
-            this.addThermalStatusListener(mContext.getMainExecutor(), listener);
-        }
+        this.addThermalStatusListener(mContext.getMainExecutor(), listener);
     }
 
     /**
@@ -1934,35 +1926,29 @@
             @NonNull OnThermalStatusChangedListener listener) {
         Preconditions.checkNotNull(listener, "listener cannot be null");
         Preconditions.checkNotNull(executor, "executor cannot be null");
-        synchronized (this) {
-            if (mThermalService == null) {
-                mThermalService = IThermalService.Stub.asInterface(
-                        ServiceManager.getService(Context.THERMAL_SERVICE));
-            }
-            Preconditions.checkArgument(!mListenerMap.containsKey(listener),
-                    "Listener already registered: " + listener);
-            IThermalStatusListener internalListener = new IThermalStatusListener.Stub() {
-                @Override
-                public void onStatusChange(int status) {
-                    final long token = Binder.clearCallingIdentity();
-                    try {
-                        executor.execute(() -> {
-                            listener.onThermalStatusChanged(status);
-                        });
-                    } finally {
-                        Binder.restoreCallingIdentity(token);
-                    }
+        Preconditions.checkArgument(!mListenerMap.containsKey(listener),
+                "Listener already registered: " + listener);
+        IThermalStatusListener internalListener = new IThermalStatusListener.Stub() {
+            @Override
+            public void onStatusChange(int status) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> {
+                        listener.onThermalStatusChanged(status);
+                    });
+                } finally {
+                    Binder.restoreCallingIdentity(token);
                 }
-            };
-            try {
-                if (mThermalService.registerThermalStatusListener(internalListener)) {
-                    mListenerMap.put(listener, internalListener);
-                } else {
-                    throw new RuntimeException("Listener failed to set");
-                }
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
             }
+        };
+        try {
+            if (mThermalService.registerThermalStatusListener(internalListener)) {
+                mListenerMap.put(listener, internalListener);
+            } else {
+                throw new RuntimeException("Listener failed to set");
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -1973,22 +1959,72 @@
      */
     public void removeThermalStatusListener(@NonNull OnThermalStatusChangedListener listener) {
         Preconditions.checkNotNull(listener, "listener cannot be null");
-        synchronized (this) {
-            if (mThermalService == null) {
-                mThermalService = IThermalService.Stub.asInterface(
-                        ServiceManager.getService(Context.THERMAL_SERVICE));
+        IThermalStatusListener internalListener = mListenerMap.get(listener);
+        Preconditions.checkArgument(internalListener != null, "Listener was not added");
+        try {
+            if (mThermalService.unregisterThermalStatusListener(internalListener)) {
+                mListenerMap.remove(listener);
+            } else {
+                throw new RuntimeException("Listener failed to remove");
             }
-            IThermalStatusListener internalListener = mListenerMap.get(listener);
-            Preconditions.checkArgument(internalListener != null, "Listener was not added");
-            try {
-                if (mThermalService.unregisterThermalStatusListener(internalListener)) {
-                    mListenerMap.remove(listener);
-                } else {
-                    throw new RuntimeException("Listener failed to remove");
-                }
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @CurrentTimeMillisLong
+    private final AtomicLong mLastHeadroomUpdate = new AtomicLong(0L);
+    private static final int MINIMUM_HEADROOM_TIME_MILLIS = 500;
+
+    /**
+     * Provides an estimate of how much thermal headroom the device currently has before hitting
+     * severe throttling.
+     *
+     * Note that this only attempts to track the headroom of slow-moving sensors, such as the skin
+     * temperature sensor. This means that there is no benefit to calling this function more
+     * frequently than about once per second, and attempts to call significantly more frequently may
+     * result in the function returning {@code NaN}.
+     *
+     * In addition, in order to be able to provide an accurate forecast, the system does not attempt
+     * to forecast until it has multiple temperature samples from which to extrapolate. This should
+     * only take a few seconds from the time of the first call, but during this time, no forecasting
+     * will occur, and the current headroom will be returned regardless of the value of
+     * {@code forecastSeconds}.
+     *
+     * The value returned is a non-negative float that represents how much of the thermal envelope
+     * is in use (or is forecasted to be in use). A value of 1.0 indicates that the device is (or
+     * will be) throttled at {@link #THERMAL_STATUS_SEVERE}. Such throttling can affect the CPU,
+     * GPU, and other subsystems. Values may exceed 1.0, but there is no implied mapping to specific
+     * thermal status levels beyond that point. This means that values greater than 1.0 may
+     * correspond to {@link #THERMAL_STATUS_SEVERE}, but may also represent heavier throttling.
+     *
+     * A value of 0.0 corresponds to a fixed distance from 1.0, but does not correspond to any
+     * particular thermal status or temperature. Values on (0.0, 1.0] may be expected to scale
+     * linearly with temperature, though temperature changes over time are typically not linear.
+     * Negative values will be clamped to 0.0 before returning.
+     *
+     * @param forecastSeconds how many seconds in the future to forecast. Given that device
+     *                        conditions may change at any time, forecasts from further in the
+     *                        future will likely be less accurate than forecasts in the near future.
+     * @return a value greater than or equal to 0.0 where 1.0 indicates the SEVERE throttling
+     *         threshold, as described above. Returns NaN if the device does not support this
+     *         functionality or if this function is called significantly faster than once per
+     *         second.
+     */
+    public float getThermalHeadroom(@IntRange(from = 0, to = 60) int forecastSeconds) {
+        // Rate-limit calls into the thermal service
+        long now = SystemClock.elapsedRealtime();
+        long timeSinceLastUpdate = now - mLastHeadroomUpdate.get();
+        if (timeSinceLastUpdate < MINIMUM_HEADROOM_TIME_MILLIS) {
+            return Float.NaN;
+        }
+
+        try {
+            float forecast = mThermalService.getThermalHeadroom(forecastSeconds);
+            mLastHeadroomUpdate.set(SystemClock.elapsedRealtime());
+            return forecast;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index e78aef5..4bd31f8 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -16,6 +16,7 @@
 
 package android.os.storage;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.IVold;
 
@@ -124,4 +125,14 @@
      * legacy storage, {@code false} otherwise.
      */
     public abstract boolean hasLegacyExternalStorage(int uid);
+
+    /**
+     * Makes sure app-private data directories on external storage are setup correctly
+     * after an application is installed or upgraded. The main use for this is OBB dirs,
+     * which can be created/modified by the installer.
+     *
+     * @param packageName the package name of the package
+     * @param uid the uid of the package
+     */
+    public abstract void prepareAppDataAfterInstall(@NonNull String packageName, int uid);
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f86c971..4523acb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8595,6 +8595,15 @@
         public static final String NOTIFICATION_HISTORY_ENABLED = "notification_history_enabled";
 
         /**
+         * When enabled conversations marked as favorites will be set to bubble.
+         *
+         * The value 1 - enable, 0 - disable
+         * @hide
+         */
+        public static final String BUBBLE_IMPORTANT_CONVERSATIONS
+                = "bubble_important_conversations";
+
+        /**
          * Whether notifications are dismissed by a right-to-left swipe (instead of a left-to-right
          * swipe).
          *
@@ -9084,26 +9093,34 @@
          * Set to one of {@link #WIFI_SLEEP_POLICY_DEFAULT},
          * {@link #WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED}, or
          * {@link #WIFI_SLEEP_POLICY_NEVER}.
+         * @deprecated This is no longer used or set by the platform.
          */
+        @Deprecated
         public static final String WIFI_SLEEP_POLICY = "wifi_sleep_policy";
 
         /**
          * Value for {@link #WIFI_SLEEP_POLICY} to use the default Wi-Fi sleep
          * policy, which is to sleep shortly after the turning off
          * according to the {@link #STAY_ON_WHILE_PLUGGED_IN} setting.
+         * @deprecated This is no longer used by the platform.
          */
+        @Deprecated
         public static final int WIFI_SLEEP_POLICY_DEFAULT = 0;
 
         /**
          * Value for {@link #WIFI_SLEEP_POLICY} to use the default policy when
          * the device is on battery, and never go to sleep when the device is
          * plugged in.
+         * @deprecated This is no longer used by the platform.
          */
+        @Deprecated
         public static final int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1;
 
         /**
          * Value for {@link #WIFI_SLEEP_POLICY} to never go to sleep.
+         * @deprecated This is no longer used by the platform.
          */
+        @Deprecated
         public static final int WIFI_SLEEP_POLICY_NEVER = 2;
 
         /**
@@ -10197,7 +10214,9 @@
        /**
         * Delay (in seconds) before repeating the Wi-Fi networks available notification.
         * Connecting to a network will reset the timer.
+        * @deprecated This is no longer used or set by the platform.
         */
+       @Deprecated
        public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
                "wifi_networks_available_repeat_delay";
 
@@ -10227,7 +10246,9 @@
        /**
         * When the number of open networks exceeds this number, the
         * least-recently-used excess networks will be removed.
+        * @deprecated This is no longer used or set by the platform.
         */
+       @Deprecated
        public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
 
        /**
@@ -10238,8 +10259,8 @@
        /**
         * Setting to allow scans to be enabled even wifi is turned off for connectivity.
         * @hide
+        * @deprecated To be removed.
         */
-       @SystemApi
        public static final String WIFI_SCAN_ALWAYS_AVAILABLE =
                 "wifi_scan_always_enabled";
 
@@ -10248,8 +10269,8 @@
          *
          * Type: int (0 for false, 1 for true)
          * @hide
+         * @deprecated To be removed.
          */
-        @SystemApi
         public static final String WIFI_P2P_PENDING_FACTORY_RESET =
                 "wifi_p2p_pending_factory_reset";
 
@@ -10258,8 +10279,8 @@
          *
          * Type: int (0 for false, 1 for true)
          * @hide
+         * @deprecated To be removed.
          */
-        @SystemApi
         public static final String SOFT_AP_TIMEOUT_ENABLED = "soft_ap_timeout_enabled";
 
         /**
@@ -10304,6 +10325,7 @@
          * Most readers of this setting should simply check if value == 1 to determined the
          * enabled state.
          * @hide
+         * @deprecated To be removed.
          */
         public static final String NETWORK_RECOMMENDATIONS_ENABLED =
                 "network_recommendations_enabled";
@@ -10343,13 +10365,11 @@
 
         /**
          * Whether wifi scan throttle is enabled or not.
-         * This is intended to be used via adb commands or a menu in developer option to turn off
-         * the default wifi scan throttling mechanism for apps.
          *
          * Type: int (0 for false, 1 for true)
          * @hide
+         * @deprecated To be removed.
          */
-        @SystemApi
         public static final String WIFI_SCAN_THROTTLE_ENABLED = "wifi_scan_throttle_enabled";
 
         /**
@@ -10451,8 +10471,8 @@
         * Setting to enable verbose logging in Wi-Fi; disabled by default, and setting to 1
         * will enable it. In the future, additional values may be supported.
         * @hide
+        * @deprecated To be removed.
         */
-       @SystemApi
        public static final String WIFI_VERBOSE_LOGGING_ENABLED =
                "wifi_verbose_logging_enabled";
 
@@ -10477,8 +10497,8 @@
          * Default values are provided by code or device configurations.
          * Errors in the parameters will cause the entire setting to be ignored.
          * @hide
+         * @deprecated This is no longer used or set by the platform.
          */
-        @SystemApi
         public static final String WIFI_SCORE_PARAMS =
                 "wifi_score_params";
 
@@ -10520,8 +10540,8 @@
        /**
         * The Wi-Fi peer-to-peer device name
         * @hide
+        * @deprecated To be removed.
         */
-       @SystemApi
        public static final String WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name";
 
        /**
diff --git a/core/java/android/service/notification/ConversationChannelWrapper.java b/core/java/android/service/notification/ConversationChannelWrapper.java
index 9847695..ab465ab 100644
--- a/core/java/android/service/notification/ConversationChannelWrapper.java
+++ b/core/java/android/service/notification/ConversationChannelWrapper.java
@@ -33,6 +33,8 @@
     private CharSequence mGroupLabel;
     private CharSequence mParentChannelLabel;
     private ShortcutInfo mShortcutInfo;
+    private String mPkg;
+    private int mUid;
 
     public ConversationChannelWrapper() {}
 
@@ -41,6 +43,8 @@
         mGroupLabel = in.readCharSequence();
         mParentChannelLabel = in.readCharSequence();
         mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader());
+        mPkg = in.readStringNoHelper();
+        mUid = in.readInt();
     }
 
     @Override
@@ -49,6 +53,8 @@
         dest.writeCharSequence(mGroupLabel);
         dest.writeCharSequence(mParentChannelLabel);
         dest.writeParcelable(mShortcutInfo, flags);
+        dest.writeStringNoHelper(mPkg);
+        dest.writeInt(mUid);
     }
 
     @Override
@@ -103,6 +109,22 @@
         mShortcutInfo = shortcutInfo;
     }
 
+    public String getPkg() {
+        return mPkg;
+    }
+
+    public void setPkg(String pkg) {
+        mPkg = pkg;
+    }
+
+    public int getUid() {
+        return mUid;
+    }
+
+    public void setUid(int uid) {
+        mUid = uid;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -111,12 +133,14 @@
         return Objects.equals(getNotificationChannel(), that.getNotificationChannel()) &&
                 Objects.equals(getGroupLabel(), that.getGroupLabel()) &&
                 Objects.equals(getParentChannelLabel(), that.getParentChannelLabel()) &&
-                Objects.equals(getShortcutInfo(), that.getShortcutInfo());
+                Objects.equals(getShortcutInfo(), that.getShortcutInfo()) &&
+                Objects.equals(getPkg(), that.getPkg()) &&
+                getUid() == that.getUid();
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(getNotificationChannel(), getGroupLabel(), getParentChannelLabel(),
-                getShortcutInfo());
+                getShortcutInfo(), getPkg(), getUid());
     }
 }
diff --git a/core/java/android/view/CutoutSpecification.java b/core/java/android/view/CutoutSpecification.java
index d21a952..850e9fc 100644
--- a/core/java/android/view/CutoutSpecification.java
+++ b/core/java/android/view/CutoutSpecification.java
@@ -406,9 +406,7 @@
                     }
                     currentIndex += RIGHT_MARKER.length();
                 } else if (specWithoutDp.startsWith(BOTTOM_MARKER, currentIndex)) {
-                    if (!mPositionFromCenterVertical) {
-                        parseSvgPathSpec(region, sb.toString());
-                    }
+                    parseSvgPathSpec(region, sb.toString());
                     currentIndex += BOTTOM_MARKER.length();
 
                     /* prepare to parse the rest path */
@@ -416,9 +414,7 @@
                     mBindBottomCutout = true;
                     mPositionFromBottom = true;
                 } else if (specWithoutDp.startsWith(CENTER_VERTICAL_MARKER, currentIndex)) {
-                    if (!mPositionFromBottom) {
-                        parseSvgPathSpec(region, sb.toString());
-                    }
+                    parseSvgPathSpec(region, sb.toString());
                     currentIndex += CENTER_VERTICAL_MARKER.length();
 
                     /* prepare to parse the rest path */
@@ -431,14 +427,16 @@
                     /* prepare to parse the rest path */
                     resetStatus(sb);
                 } else if (specWithoutDp.startsWith(BIND_LEFT_CUTOUT_MARKER, currentIndex)) {
-                    if (!mBindBottomCutout && !mBindRightCutout) {
-                        mBindLeftCutout = true;
-                    }
+                    mBindBottomCutout = false;
+                    mBindRightCutout = false;
+                    mBindLeftCutout = true;
+
                     currentIndex += BIND_LEFT_CUTOUT_MARKER.length();
                 } else if (specWithoutDp.startsWith(BIND_RIGHT_CUTOUT_MARKER, currentIndex)) {
-                    if (!mBindBottomCutout && !mBindLeftCutout) {
-                        mBindRightCutout = true;
-                    }
+                    mBindBottomCutout = false;
+                    mBindLeftCutout = false;
+                    mBindRightCutout = true;
+
                     currentIndex += BIND_RIGHT_CUTOUT_MARKER.length();
                 } else {
                     currentIndex += 1;
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index b9868a7..3047385 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -28,6 +28,7 @@
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.hardware.display.DeviceProductInfo;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -70,6 +71,13 @@
     public DisplayAddress address;
 
     /**
+     * Product-specific information about the display or the directly connected device on the
+     * display chain. For example, if the display is transitively connected, this field may contain
+     * product information about the intermediate device.
+     */
+    public DeviceProductInfo deviceProductInfo;
+
+    /**
      * The human-readable name of the display.
      */
     public String name;
@@ -297,6 +305,7 @@
                 && type == other.type
                 && displayId == other.displayId
                 && Objects.equals(address, other.address)
+                && Objects.equals(deviceProductInfo, other.deviceProductInfo)
                 && Objects.equals(uniqueId, other.uniqueId)
                 && appWidth == other.appWidth
                 && appHeight == other.appHeight
@@ -336,6 +345,7 @@
         type = other.type;
         displayId = other.displayId;
         address = other.address;
+        deviceProductInfo = other.deviceProductInfo;
         name = other.name;
         uniqueId = other.uniqueId;
         appWidth = other.appWidth;
@@ -373,6 +383,7 @@
         type = source.readInt();
         displayId = source.readInt();
         address = source.readParcelable(null);
+        deviceProductInfo = source.readParcelable(null);
         name = source.readString();
         appWidth = source.readInt();
         appHeight = source.readInt();
@@ -418,6 +429,7 @@
         dest.writeInt(type);
         dest.writeInt(displayId);
         dest.writeParcelable(address, flags);
+        dest.writeParcelable(deviceProductInfo, flags);
         dest.writeString(name);
         dest.writeInt(appWidth);
         dest.writeInt(appHeight);
@@ -645,6 +657,8 @@
         if (address != null) {
             sb.append(", address ").append(address);
         }
+        sb.append(", deviceProductInfo ");
+        sb.append(deviceProductInfo);
         sb.append(", state ");
         sb.append(Display.stateToString(state));
         if (ownerUid != 0 || ownerPackageName != null) {
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 360dedd..58e5b2d 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.annotation.IntDef;
 import android.annotation.RequiresPermission;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -28,6 +29,8 @@
 import android.os.Parcelable;
 import android.os.Vibrator;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -138,6 +141,19 @@
      */
     public static final int SOURCE_CLASS_JOYSTICK = 0x00000010;
 
+    /** @hide */
+    @IntDef(flag = true, prefix = { "SOURCE_CLASS_" }, value = {
+            SOURCE_CLASS_NONE,
+            SOURCE_CLASS_BUTTON,
+            SOURCE_CLASS_POINTER,
+            SOURCE_CLASS_POINTER,
+            SOURCE_CLASS_TRACKBALL,
+            SOURCE_CLASS_POSITION,
+            SOURCE_CLASS_JOYSTICK
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface InputSourceClass {}
+
     /**
      * The input source is unknown.
      */
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index cf48c52..29371b0 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -41,6 +41,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.hardware.display.DeviceProductInfo;
 import android.hardware.display.DisplayedContentSample;
 import android.hardware.display.DisplayedContentSamplingAttributes;
 import android.os.Build;
@@ -1286,12 +1287,14 @@
         public boolean isInternal;
         public float density;
         public boolean secure;
+        public DeviceProductInfo deviceProductInfo;
 
         @Override
         public String toString() {
             return "DisplayInfo{isInternal=" + isInternal
                     + ", density=" + density
-                    + ", secure=" + secure + "}";
+                    + ", secure=" + secure
+                    + ", deviceProductInfo=" + deviceProductInfo + "}";
         }
     }
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b9be33c..4c7307e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -110,6 +110,7 @@
 import android.view.AccessibilityIterators.TextSegmentIterator;
 import android.view.AccessibilityIterators.WordTextSegmentIterator;
 import android.view.ContextMenu.ContextMenuInfo;
+import android.view.InputDevice.InputSourceClass;
 import android.view.Window.OnContentApplyWindowInsetsListener;
 import android.view.WindowInsets.Type;
 import android.view.WindowInsetsAnimation.Bounds;
@@ -5205,6 +5206,14 @@
     private int mExplicitStyle;
 
     /**
+     * Specifies which input source classes should provide unbuffered input events to this view
+     *
+     * @see View#requestUnbufferedDispatch(int)
+     */
+    @InputSourceClass
+    int mUnbufferedInputSource = InputDevice.SOURCE_CLASS_NONE;
+
+    /**
      * Simple constructor to use when creating a view from code.
      *
      * @param context The Context the view is running in, through which it can
@@ -8013,6 +8022,10 @@
             mAttachInfo.mKeyDispatchState.reset(this);
         }
 
+        if (mParent != null) {
+            mParent.onDescendantUnbufferedRequested();
+        }
+
         notifyEnterOrExitForAutoFillIfNeeded(gainFocus);
     }
 
@@ -15820,12 +15833,17 @@
      * system not batch {@link MotionEvent}s but instead deliver them as soon as they're
      * available. This method should only be called for touch events.
      *
-     * <p class="note">This api is not intended for most applications. Buffered dispatch
+     * <p class="note">This API is not intended for most applications. Buffered dispatch
      * provides many of benefits, and just requesting unbuffered dispatch on most MotionEvent
      * streams will not improve your input latency. Side effects include: increased latency,
      * jittery scrolls and inability to take advantage of system resampling. Talk to your input
      * professional to see if {@link #requestUnbufferedDispatch(MotionEvent)} is right for
      * you.</p>
+     *
+     * To receive unbuffered events for arbitrary input device source classes, use
+     * {@link #requestUnbufferedDispatch(int)},
+     *
+     * @see View#requestUnbufferedDispatch(int)
      */
     public final void requestUnbufferedDispatch(MotionEvent event) {
         final int action = event.getAction();
@@ -15837,6 +15855,27 @@
         mAttachInfo.mUnbufferedDispatchRequested = true;
     }
 
+    /**
+     * Request unbuffered dispatch of the given event source class to this view.
+     * This is similar to {@link View#requestUnbufferedDispatch(MotionEvent)}, but does not
+     * automatically terminate, and allows the specification of arbitrary input source classes.
+     *
+     * @param source The combined input source class to request unbuffered dispatch for. All
+     *               events coming from these source classes will not be buffered. Set to
+     *               {@link InputDevice#SOURCE_CLASS_NONE} in order to return to default behaviour.
+     *
+     * @see View#requestUnbufferedDispatch(MotionEvent)
+     */
+    public final void requestUnbufferedDispatch(@InputSourceClass int source) {
+        if (mUnbufferedInputSource == source) {
+            return;
+        }
+        mUnbufferedInputSource = source;
+        if (mParent != null) {
+            mParent.onDescendantUnbufferedRequested();
+        }
+    }
+
     private boolean hasSize() {
         return (mBottom > mTop) && (mRight > mLeft);
     }
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 0367536..b6c46be 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3692,6 +3692,31 @@
             }
             childrenForAccessibility.clear();
         }
+        info.setAvailableExtraData(Collections.singletonList(
+                AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY));
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @param info The info to which to add the extra data. Never {@code null}.
+     * @param extraDataKey A key specifying the type of extra data to add to the info. The
+     *                     extra data should be added to the {@link Bundle} returned by
+     *                     the info's {@link AccessibilityNodeInfo#getExtras} method. Never
+     *                     {@code null}.
+     * @param arguments A {@link Bundle} holding any arguments relevant for this request. May be
+     *                  {@code null} if the service provided no arguments.
+     *
+     */
+    @Override
+    public void addExtraDataToAccessibilityNodeInfo(@NonNull AccessibilityNodeInfo info,
+            @NonNull String extraDataKey, @Nullable Bundle arguments) {
+        if (extraDataKey.equals(AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY)) {
+            final AccessibilityNodeInfo.ExtraRenderingInfo extraRenderingInfo =
+                    AccessibilityNodeInfo.ExtraRenderingInfo.obtain();
+            extraRenderingInfo.setLayoutParams(getLayoutParams().width, getLayoutParams().height);
+            info.setExtraRenderingInfo(extraRenderingInfo);
+        }
     }
 
     @Override
@@ -9004,4 +9029,30 @@
             getChildAt(i).encode(encoder);
         }
     }
+
+    /** @hide */
+    @Override
+    public final void onDescendantUnbufferedRequested() {
+        // First look at the focused child for focused events
+        int focusedChildNonPointerSource = InputDevice.SOURCE_CLASS_NONE;
+        if (mFocused != null) {
+            focusedChildNonPointerSource = mFocused.mUnbufferedInputSource
+                    & (~InputDevice.SOURCE_CLASS_POINTER);
+        }
+        mUnbufferedInputSource = focusedChildNonPointerSource;
+
+        // Request unbuffered dispatch for pointer events for this view if any child requested
+        // unbuffered dispatch for pointer events. This is because we can't expect that the pointer
+        // source would dispatch to the focused view.
+        for (int i = 0; i < mChildrenCount; i++) {
+            final View child = mChildren[i];
+            if ((child.mUnbufferedInputSource & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+                mUnbufferedInputSource |= InputDevice.SOURCE_CLASS_POINTER;
+                break;
+            }
+        }
+        if (mParent != null) {
+            mParent.onDescendantUnbufferedRequested();
+        }
+    }
 }
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index f25206d..775c15e 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -675,4 +675,18 @@
      */
     default void subtractObscuredTouchableRegion(Region touchableRegion, View view) {
     }
+
+    /**
+     * Unbuffered dispatch has been requested by a child of this view parent.
+     * This method is called by the View hierarchy to signal ancestors that a View needs to
+     * request unbuffered dispatch.
+     *
+     * @see View#requestUnbufferedDispatch(int)
+     * @hide
+     */
+    default void onDescendantUnbufferedRequested() {
+        if (getParent() != null) {
+            getParent().onDescendantUnbufferedRequested();
+        }
+    }
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 043e5be..204d2c8 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -18,6 +18,7 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.view.InputDevice.SOURCE_CLASS_NONE;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.View.PFLAG_DRAW_ANIMATION;
@@ -116,6 +117,7 @@
 import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.util.TypedValue;
+import android.view.InputDevice.InputSourceClass;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl.Transaction;
@@ -499,6 +501,10 @@
     int mPendingInputEventCount;
     boolean mProcessInputEventsScheduled;
     boolean mUnbufferedInputDispatch;
+    boolean mUnbufferedInputDispatchBySource;
+    @InputSourceClass
+    int mUnbufferedInputSource = SOURCE_CLASS_NONE;
+
     String mPendingInputEventQueueLengthCounterName = "pq";
 
     InputStage mFirstInputStage;
@@ -1849,7 +1855,7 @@
             mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
             mChoreographer.postCallback(
                     Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
-            if (!mUnbufferedInputDispatch) {
+            if (!mUnbufferedInputDispatch && !mUnbufferedInputDispatchBySource) {
                 scheduleConsumeBatchedInput();
             }
             notifyRendererOfFramePending();
@@ -7505,6 +7511,9 @@
                 writer.print(mTraversalScheduled);
         writer.print(innerPrefix); writer.print("mIsAmbientMode=");
                 writer.print(mIsAmbientMode);
+        writer.print(innerPrefix); writer.print("mUnbufferedInputSource=");
+        writer.print(Integer.toHexString(mUnbufferedInputSource));
+
         if (mTraversalScheduled) {
             writer.print(" (barrier="); writer.print(mTraversalBarrier); writer.println(")");
         } else {
@@ -8109,6 +8118,7 @@
         @Override
         public void onInputEvent(InputEvent event) {
             Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");
+            processUnbufferedRequest(event);
             List<InputEvent> processedEvents;
             try {
                 processedEvents =
@@ -8134,7 +8144,7 @@
 
         @Override
         public void onBatchedInputEventPending() {
-            if (mUnbufferedInputDispatch) {
+            if (mUnbufferedInputDispatch || mUnbufferedInputDispatchBySource) {
                 super.onBatchedInputEventPending();
             } else {
                 scheduleConsumeBatchedInput();
@@ -8151,6 +8161,17 @@
             unscheduleConsumeBatchedInput();
             super.dispose();
         }
+
+        private void processUnbufferedRequest(InputEvent event) {
+            if (!(event instanceof MotionEvent)) {
+                return;
+            }
+            mUnbufferedInputDispatchBySource =
+                    (event.getSource() & mUnbufferedInputSource) != SOURCE_CLASS_NONE;
+            if (mUnbufferedInputDispatchBySource && mConsumeBatchedInputScheduled) {
+                scheduleConsumeBatchedInputImmediately();
+            }
+        }
     }
     WindowInputEventReceiver mInputEventReceiver;
 
@@ -9601,4 +9622,9 @@
             return mSurfaceControl;
         }
     }
+
+    @Override
+    public void onDescendantUnbufferedRequested() {
+        mUnbufferedInputSource = mView.mUnbufferedInputSource;
+    }
 }
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index bf2de14..eb4f9db 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -50,8 +50,12 @@
 import android.util.Log;
 import android.util.LongArray;
 import android.util.Pools.SynchronizedPool;
+import android.util.Size;
+import android.util.TypedValue;
 import android.view.TouchDelegate;
 import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
 
 import com.android.internal.R;
 import com.android.internal.util.CollectionUtils;
@@ -634,6 +638,25 @@
     public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH =
             "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
 
+    /**
+     * Key used to request extra data for accessibility scanning tool's purposes.
+     * The key requests that a {@link AccessibilityNodeInfo.ExtraRenderingInfo} be added to this
+     * info. This request is made with {@link #refreshWithExtraData(String, Bundle)} without
+     * argument.
+     * <p>
+     * The data can be retrieved from the {@link ExtraRenderingInfo} returned by
+     * {@link #getExtraRenderingInfo()} using {@link ExtraRenderingInfo#getLayoutParams},
+     * {@link ExtraRenderingInfo#getTextSizeInPx()} and
+     * {@link ExtraRenderingInfo#getTextSizeUnit()}. For layout params, it is supported by both
+     * {@link TextView} and {@link ViewGroup}. For text size and unit, it is only supported by
+     * {@link TextView}.
+     *
+     * @see #refreshWithExtraData(String, Bundle)
+     */
+
+    public static final String EXTRA_DATA_RENDERING_INFO_KEY =
+            "android.view.accessibility.extra.DATA_RENDERING_INFO_KEY";
+
     /** @hide */
     public static final String EXTRA_DATA_REQUESTED_KEY =
             "android.view.accessibility.AccessibilityNodeInfo.extra_data_requested";
@@ -804,6 +827,8 @@
 
     private TouchDelegateInfo mTouchDelegateInfo;
 
+    private ExtraRenderingInfo mExtraRenderingInfo;
+
     private IBinder mLeashedChild;
     private IBinder mLeashedParent;
     private long mLeashedParentNodeId = UNDEFINED_NODE_ID;
@@ -991,6 +1016,7 @@
      * @param extraDataKey The extra data requested. Data that must be requested
      *                     with this mechanism is generally expensive to retrieve, so should only be
      *                     requested when needed. See
+     *                     {@link #EXTRA_DATA_RENDERING_INFO_KEY},
      *                     {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY} and
      *                     {@link #getAvailableExtraData()}.
      * @param args A bundle of arguments for the request. These depend on the particular request.
@@ -1547,6 +1573,7 @@
      * {@link #refreshWithExtraData(String, Bundle)}.
      *
      * @return An unmodifiable list of keys corresponding to extra data that can be requested.
+     * @see #EXTRA_DATA_RENDERING_INFO_KEY
      * @see #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
      */
     public List<String> getAvailableExtraData() {
@@ -2375,6 +2402,32 @@
     }
 
     /**
+     * Gets the conformance info if the node is meant to be refreshed with extra data.
+     *
+     * @return The conformance info.
+     */
+    @Nullable
+    public ExtraRenderingInfo getExtraRenderingInfo() {
+        return mExtraRenderingInfo;
+    }
+
+    /**
+     * Sets the conformance info if the node is meant to be refreshed with extra data.
+     * <p>
+     *   <strong>Note:</strong> Cannot be called from an
+     *   {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     *
+     * @param extraRenderingInfo The conformance info.
+     * @hide
+     */
+    public void setExtraRenderingInfo(@NonNull ExtraRenderingInfo extraRenderingInfo) {
+        enforceNotSealed();
+        mExtraRenderingInfo = extraRenderingInfo;
+    }
+
+    /**
      * Gets if the content of this node is invalid. For example,
      * a date is not well-formed.
      *
@@ -3695,6 +3748,10 @@
             nonDefaultFields |= bitAt(fieldIndex);
         }
         fieldIndex++;
+        if (!Objects.equals(mExtraRenderingInfo, DEFAULT.mExtraRenderingInfo)) {
+            nonDefaultFields |= bitAt(fieldIndex);
+        }
+        fieldIndex++;
         if (mLeashedChild != DEFAULT.mLeashedChild) {
             nonDefaultFields |= bitAt(fieldIndex);
         }
@@ -3833,6 +3890,12 @@
         }
 
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
+            parcel.writeValue(mExtraRenderingInfo.getLayoutParams());
+            parcel.writeFloat(mExtraRenderingInfo.getTextSizeInPx());
+            parcel.writeInt(mExtraRenderingInfo.getTextSizeUnit());
+        }
+
+        if (isBitSet(nonDefaultFields, fieldIndex++)) {
             parcel.writeStrongBinder(mLeashedChild);
         }
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
@@ -3941,6 +4004,9 @@
         if (mCollectionItemInfo != null) mCollectionItemInfo.recycle();
         mCollectionItemInfo =  (other.mCollectionItemInfo != null)
                 ? CollectionItemInfo.obtain(other.mCollectionItemInfo) : null;
+        if (mExtraRenderingInfo != null) mExtraRenderingInfo.recycle();
+        mExtraRenderingInfo = (other.mExtraRenderingInfo != null)
+                ? ExtraRenderingInfo.obtain(other.mExtraRenderingInfo) : null;
     }
 
     private void initCopyInfos(AccessibilityNodeInfo other) {
@@ -3955,6 +4021,9 @@
         mCollectionItemInfo = (cii == null)  ? null
                 : new CollectionItemInfo(cii.mRowIndex, cii.mRowSpan, cii.mColumnIndex,
                                          cii.mColumnSpan, cii.mHeading, cii.mSelected);
+        ExtraRenderingInfo ti = other.mExtraRenderingInfo;
+        mExtraRenderingInfo = (ti == null) ? null
+                : new ExtraRenderingInfo(ti);
     }
 
     /**
@@ -4083,6 +4152,14 @@
         }
 
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
+            if (mExtraRenderingInfo != null) mExtraRenderingInfo.recycle();
+            mExtraRenderingInfo = ExtraRenderingInfo.obtain();
+            mExtraRenderingInfo.mLayoutParams = (Size) parcel.readValue(null);
+            mExtraRenderingInfo.mTextSizeInPx = parcel.readFloat();
+            mExtraRenderingInfo.mTextSizeUnit = parcel.readInt();
+        }
+
+        if (isBitSet(nonDefaultFields, fieldIndex++)) {
             mLeashedChild = parcel.readStrongBinder();
         }
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
@@ -5679,6 +5756,134 @@
     }
 
     /**
+     * Class with information of a view useful to evaluate accessibility needs. Developers can
+     * refresh the node with the key {@link #EXTRA_DATA_RENDERING_INFO_KEY} to fetch the text size
+     * and unit if it is {@link TextView} and the height and the width of layout params from
+     * {@link ViewGroup} or {@link TextView}.
+     *
+     * @see #EXTRA_DATA_RENDERING_INFO_KEY
+     * @see #refreshWithExtraData(String, Bundle)
+     */
+    public static final class ExtraRenderingInfo {
+        private static final int UNDEFINED_VALUE = -1;
+        private static final int MAX_POOL_SIZE = 20;
+        private static final SynchronizedPool<ExtraRenderingInfo> sPool =
+                new SynchronizedPool<>(MAX_POOL_SIZE);
+
+        private Size mLayoutParams;
+        private float mTextSizeInPx = UNDEFINED_VALUE;
+        private int mTextSizeUnit = UNDEFINED_VALUE;
+
+        /**
+         * Obtains a pooled instance.
+         * @hide
+         */
+        @NonNull
+        public static ExtraRenderingInfo obtain() {
+            final ExtraRenderingInfo info = sPool.acquire();
+            if (info == null) {
+                return new ExtraRenderingInfo(null);
+            }
+            return info;
+        }
+
+        /** Obtains a pooled instance that is a clone of another one. */
+        private static ExtraRenderingInfo obtain(ExtraRenderingInfo other) {
+            ExtraRenderingInfo extraRenderingInfo = ExtraRenderingInfo.obtain();
+            extraRenderingInfo.mLayoutParams = other.mLayoutParams;
+            extraRenderingInfo.mTextSizeInPx = other.mTextSizeInPx;
+            extraRenderingInfo.mTextSizeUnit = other.mTextSizeUnit;
+            return extraRenderingInfo;
+        }
+
+        /**
+         * Creates a new conformance info of a view, and this new instance is initialized from
+         * the given <code>other</code>.
+         *
+         * @param other The instance to clone.
+         */
+        private ExtraRenderingInfo(@Nullable ExtraRenderingInfo other) {
+            if (other != null) {
+                mLayoutParams = other.mLayoutParams;
+                mTextSizeInPx = other.mTextSizeInPx;
+                mTextSizeUnit = other.mTextSizeUnit;
+            }
+        }
+
+        /**
+         * @return a {@link Size} stores layout height and layout width of the view,
+         *         or null otherwise.
+         */
+        public @Nullable Size getLayoutParams() {
+            return mLayoutParams;
+        }
+
+        /**
+         * Sets layout width and layout height of the view.
+         *
+         * @param width The layout width.
+         * @param height The layout height.
+         * @hide
+         */
+        public void setLayoutParams(int width, int height) {
+            mLayoutParams = new Size(width, height);
+        }
+
+        /**
+         * @return the text size of a {@code TextView}, or -1 otherwise.
+         */
+        public float getTextSizeInPx() {
+            return mTextSizeInPx;
+        }
+
+        /**
+         * Sets text size of the view.
+         *
+         * @param textSizeInPx The text size in pixels.
+         * @hide
+         */
+        public void setTextSizeInPx(float textSizeInPx) {
+            mTextSizeInPx = textSizeInPx;
+        }
+
+        /**
+         * @return the text size unit which type is {@link TypedValue#TYPE_DIMENSION} of a
+         *         {@code TextView}, or -1 otherwise.
+         *
+         * @see TypedValue#TYPE_DIMENSION
+         */
+        public int getTextSizeUnit() {
+            return mTextSizeUnit;
+        }
+
+        /**
+         * Sets text size unit of the view.
+         *
+         * @param textSizeUnit The text size unit.
+         * @hide
+         */
+        public void setTextSizeUnit(int textSizeUnit) {
+            mTextSizeUnit = textSizeUnit;
+        }
+
+        /**
+         * Recycles this instance.
+         *
+         * <p>In most situations object pooling is not beneficial, and recycling is not necessary.
+         */
+        void recycle() {
+            clear();
+            sPool.release(this);
+        }
+
+        private void clear() {
+            mLayoutParams = null;
+            mTextSizeInPx = UNDEFINED_VALUE;
+            mTextSizeUnit = UNDEFINED_VALUE;
+        }
+    }
+
+    /**
      * @see android.os.Parcelable.Creator
      */
     public static final @android.annotation.NonNull Parcelable.Creator<AccessibilityNodeInfo> CREATOR =
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0182975..815cc5c 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -17,6 +17,7 @@
 package android.widget;
 
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY;
 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH;
 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
@@ -727,6 +728,7 @@
     @UnsupportedAppUsage
     private Layout mLayout;
     private boolean mLocalesChanged = false;
+    private int mTextSizeUnit = -1;
 
     // True if setKeyListener() has been explicitly called
     private boolean mListenerChanged = false;
@@ -3842,6 +3844,7 @@
         ColorStateList mTextColorHint = null;
         ColorStateList mTextColorLink = null;
         int mTextSize = -1;
+        int mTextSizeUnit = -1;
         LocaleList mTextLocales = null;
         String mFontFamily = null;
         Typeface mFontTypeface = null;
@@ -3869,6 +3872,7 @@
                     + "    mTextColorHint:" + mTextColorHint + "\n"
                     + "    mTextColorLink:" + mTextColorLink + "\n"
                     + "    mTextSize:" + mTextSize + "\n"
+                    + "    mTextSizeUnit:" + mTextSizeUnit + "\n"
                     + "    mTextLocales:" + mTextLocales + "\n"
                     + "    mFontFamily:" + mFontFamily + "\n"
                     + "    mFontTypeface:" + mFontTypeface + "\n"
@@ -3980,6 +3984,7 @@
                 case com.android.internal.R.styleable.TextAppearance_textSize:
                     attributes.mTextSize =
                             appearance.getDimensionPixelSize(attr, attributes.mTextSize);
+                    attributes.mTextSizeUnit = appearance.peekValue(attr).getComplexUnit();
                     break;
                 case com.android.internal.R.styleable.TextAppearance_textLocale:
                     final String localeString = appearance.getString(attr);
@@ -4073,6 +4078,7 @@
         }
 
         if (attributes.mTextSize != -1) {
+            mTextSizeUnit = attributes.mTextSizeUnit;
             setRawTextSize(attributes.mTextSize, true /* shouldRequestLayout */);
         }
 
@@ -4295,6 +4301,7 @@
             r = c.getResources();
         }
 
+        mTextSizeUnit = unit;
         setRawTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()),
                 shouldRequestLayout);
     }
@@ -4315,6 +4322,17 @@
     }
 
     /**
+     * Gets the text size unit defined by the developer. It may be specified in resources or be
+     * passed as the unit argument of {@link #setTextSize(int, float)} at runtime.
+     *
+     * @return the dimension type of the text size unit originally defined.
+     * @see TypedValue#TYPE_DIMENSION
+     */
+    public int getTextSizeUnit() {
+        return mTextSizeUnit;
+    }
+
+    /**
      * Gets the extent by which text should be stretched horizontally.
      * This will usually be 1.0.
      * @return The horizontal scale factor.
@@ -11769,8 +11787,14 @@
                     | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
                     | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
             info.addAction(AccessibilityNodeInfo.ACTION_SET_SELECTION);
-            info.setAvailableExtraData(
-                    Arrays.asList(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY));
+            info.setAvailableExtraData(Arrays.asList(
+                    EXTRA_DATA_RENDERING_INFO_KEY,
+                    EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
+            ));
+        } else {
+            info.setAvailableExtraData(Arrays.asList(
+                    EXTRA_DATA_RENDERING_INFO_KEY
+            ));
         }
 
         if (isFocused()) {
@@ -11824,11 +11848,7 @@
     @Override
     public void addExtraDataToAccessibilityNodeInfo(
             AccessibilityNodeInfo info, String extraDataKey, Bundle arguments) {
-        // The only extra data we support requires arguments.
-        if (arguments == null) {
-            return;
-        }
-        if (extraDataKey.equals(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY)) {
+        if (arguments != null && extraDataKey.equals(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY)) {
             int positionInfoStartIndex = arguments.getInt(
                     EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX, -1);
             int positionInfoLength = arguments.getInt(
@@ -11856,6 +11876,15 @@
                 }
             }
             info.getExtras().putParcelableArray(extraDataKey, boundingRects);
+            return;
+        }
+        if (extraDataKey.equals(AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY)) {
+            final AccessibilityNodeInfo.ExtraRenderingInfo extraRenderingInfo =
+                    AccessibilityNodeInfo.ExtraRenderingInfo.obtain();
+            extraRenderingInfo.setLayoutParams(getLayoutParams().width, getLayoutParams().height);
+            extraRenderingInfo.setTextSizeInPx(getTextSize());
+            extraRenderingInfo.setTextSizeUnit(getTextSizeUnit());
+            info.setExtraRenderingInfo(extraRenderingInfo);
         }
     }
 
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 61d22d1..a201a33 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -3100,7 +3100,7 @@
             final ViewGroup viewGroup = (ViewGroup) holder.itemView;
             int start = getListPosition(position);
             int startType = getRowType(start);
-            if (viewGroup.getForeground() == null) {
+            if (viewGroup.getForeground() == null && position > 0) {
                 viewGroup.setForeground(
                         getResources().getDrawable(R.drawable.chooser_row_layer_list, null));
             }
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index f168215..96bfe73 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1566,6 +1566,7 @@
         viewPager.setVisibility(View.VISIBLE);
         tabHost.setCurrentTab(mMultiProfilePagerAdapter.getCurrentPage());
         mMultiProfilePagerAdapter.setOnProfileSelectedListener(tabHost::setCurrentTab);
+        findViewById(R.id.resolver_tab_divider).setVisibility(View.VISIBLE);
     }
 
     private void resetTabsHeaderStyle(TabWidget tabWidget) {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 3f81b11..741ce82 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -37,6 +37,7 @@
 #include <stdio.h>
 #include <system/graphics.h>
 #include <ui/ConfigStoreTypes.h>
+#include <ui/DeviceProductInfo.h>
 #include <ui/DisplayConfig.h>
 #include <ui/DisplayInfo.h>
 #include <ui/DisplayedFrameStats.h>
@@ -65,9 +66,19 @@
 static struct {
     jclass clazz;
     jmethodID ctor;
+} gIntegerClassInfo;
+
+static jobject toInteger(JNIEnv* env, int32_t i) {
+    return env->NewObject(gIntegerClassInfo.clazz, gIntegerClassInfo.ctor, i);
+}
+
+static struct {
+    jclass clazz;
+    jmethodID ctor;
     jfieldID isInternal;
     jfieldID density;
     jfieldID secure;
+    jfieldID deviceProductInfo;
 } gDisplayInfoClassInfo;
 
 static struct {
@@ -111,6 +122,16 @@
 
 static struct {
     jclass clazz;
+    jmethodID ctor;
+} gDeviceProductInfoClassInfo;
+
+static struct {
+    jclass clazz;
+    jmethodID ctor;
+} gDeviceProductInfoManufactureDateClassInfo;
+
+static struct {
+    jclass clazz;
     jmethodID builder;
 } gGraphicBufferClassInfo;
 
@@ -773,6 +794,41 @@
     }
 }
 
+static jobject convertDeviceProductInfoToJavaObject(
+        JNIEnv* env, const std::optional<DeviceProductInfo>& info) {
+    using ModelYear = android::DeviceProductInfo::ModelYear;
+    using ManufactureYear = android::DeviceProductInfo::ManufactureYear;
+    using ManufactureWeekAndYear = android::DeviceProductInfo::ManufactureWeekAndYear;
+
+    if (!info) return nullptr;
+    jstring name = env->NewStringUTF(info->name.data());
+    jstring manufacturerPnpId = env->NewStringUTF(info->manufacturerPnpId.data());
+    jobject productId = env->NewStringUTF(info->productId.data());
+    const auto& date = info->manufactureOrModelDate;
+    jobject modelYear, manufactureDate;
+    if (const auto* model = std::get_if<ModelYear>(&date)) {
+        modelYear = toInteger(env, model->year);
+        manufactureDate = nullptr;
+    } else if (const auto* manufactureWeekAndYear = std::get_if<ManufactureWeekAndYear>(&date)) {
+        modelYear = nullptr;
+        manufactureDate = env->NewObject(gDeviceProductInfoManufactureDateClassInfo.clazz,
+                                               gDeviceProductInfoManufactureDateClassInfo.ctor,
+                                               toInteger(env, manufactureWeekAndYear->week),
+                                               toInteger(env, manufactureWeekAndYear->year));
+    } else if (const auto* manufactureYear = std::get_if<ManufactureYear>(&date)) {
+        modelYear = nullptr;
+        manufactureDate = env->NewObject(gDeviceProductInfoManufactureDateClassInfo.clazz,
+                                       gDeviceProductInfoManufactureDateClassInfo.ctor,
+                                       nullptr,
+                                       toInteger(env, manufactureYear->year));
+    } else {
+        LOG_FATAL("Unknown alternative for variant DeviceProductInfo::ManufactureOrModelDate");
+    }
+
+    return env->NewObject(gDeviceProductInfoClassInfo.clazz, gDeviceProductInfoClassInfo.ctor, name,
+                          manufacturerPnpId, productId, modelYear, manufactureDate);
+}
+
 static jobject nativeGetDisplayInfo(JNIEnv* env, jclass clazz, jobject tokenObj) {
     DisplayInfo info;
     if (const auto token = ibinderForJavaObject(env, tokenObj);
@@ -785,6 +841,8 @@
                          info.connectionType == DisplayConnectionType::Internal);
     env->SetFloatField(object, gDisplayInfoClassInfo.density, info.density);
     env->SetBooleanField(object, gDisplayInfoClassInfo.secure, info.secure);
+    env->SetObjectField(object, gDisplayInfoClassInfo.deviceProductInfo,
+                        convertDeviceProductInfoToJavaObject(env, info.deviceProductInfo));
     return object;
 }
 
@@ -1527,12 +1585,19 @@
     int err = RegisterMethodsOrDie(env, "android/view/SurfaceControl",
             sSurfaceControlMethods, NELEM(sSurfaceControlMethods));
 
+    jclass integerClass = FindClassOrDie(env, "java/lang/Integer");
+    gIntegerClassInfo.clazz = MakeGlobalRefOrDie(env, integerClass);
+    gIntegerClassInfo.ctor = GetMethodIDOrDie(env, gIntegerClassInfo.clazz, "<init>", "(I)V");
+
     jclass infoClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayInfo");
     gDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, infoClazz);
     gDisplayInfoClassInfo.ctor = GetMethodIDOrDie(env, infoClazz, "<init>", "()V");
     gDisplayInfoClassInfo.isInternal = GetFieldIDOrDie(env, infoClazz, "isInternal", "Z");
     gDisplayInfoClassInfo.density = GetFieldIDOrDie(env, infoClazz, "density", "F");
     gDisplayInfoClassInfo.secure = GetFieldIDOrDie(env, infoClazz, "secure", "Z");
+    gDisplayInfoClassInfo.deviceProductInfo =
+            GetFieldIDOrDie(env, infoClazz, "deviceProductInfo",
+                            "Landroid/hardware/display/DeviceProductInfo;");
 
     jclass configClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayConfig");
     gDisplayConfigClassInfo.clazz = MakeGlobalRefOrDie(env, configClazz);
@@ -1573,6 +1638,25 @@
     gHdrCapabilitiesClassInfo.ctor = GetMethodIDOrDie(env, hdrCapabilitiesClazz, "<init>",
             "([IFFF)V");
 
+    jclass deviceProductInfoClazz =
+            FindClassOrDie(env, "android/hardware/display/DeviceProductInfo");
+    gDeviceProductInfoClassInfo.clazz = MakeGlobalRefOrDie(env, deviceProductInfoClazz);
+    gDeviceProductInfoClassInfo.ctor =
+            GetMethodIDOrDie(env, deviceProductInfoClazz, "<init>",
+                             "(Ljava/lang/String;"
+                             "Ljava/lang/String;"
+                             "Ljava/lang/String;"
+                             "Ljava/lang/Integer;"
+                             "Landroid/hardware/display/DeviceProductInfo$ManufactureDate;)V");
+
+    jclass deviceProductInfoManufactureDateClazz =
+            FindClassOrDie(env, "android/hardware/display/DeviceProductInfo$ManufactureDate");
+    gDeviceProductInfoManufactureDateClassInfo.clazz =
+            MakeGlobalRefOrDie(env, deviceProductInfoManufactureDateClazz);
+    gDeviceProductInfoManufactureDateClassInfo.ctor =
+            GetMethodIDOrDie(env, deviceProductInfoManufactureDateClazz, "<init>",
+                             "(Ljava/lang/Integer;Ljava/lang/Integer;)V");
+
     jclass graphicsBufferClazz = FindClassOrDie(env, "android/graphics/GraphicBuffer");
     gGraphicBufferClassInfo.clazz = MakeGlobalRefOrDie(env, graphicsBufferClazz);
     gGraphicBufferClassInfo.builder = GetStaticMethodIDOrDie(env, graphicsBufferClazz,
diff --git a/core/proto/android/service/graphicsstats.proto b/core/proto/android/service/graphicsstats.proto
index 2de5b7f..bb654f0 100644
--- a/core/proto/android/service/graphicsstats.proto
+++ b/core/proto/android/service/graphicsstats.proto
@@ -33,8 +33,9 @@
 
 message GraphicsStatsProto {
     enum PipelineType {
-        GL = 0;
-        VULKAN = 1;
+        UNKNOWN = 0;
+        GL = 1;
+        VULKAN = 2;
     }
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 488698a..6530036 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4953,16 +4953,6 @@
     <permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
                 android:protectionLevel="signature|appPredictor" />
 
-    <!-- Feature Id for Country Detector. -->
-    <feature android:featureId="CountryDetector" android:label="@string/country_detector"/>
-    <!-- Feature Id for Location service. -->
-    <feature android:featureId="LocationService" android:label="@string/location_service"/>
-    <!-- Feature Id for Sensor Notification service. -->
-    <feature android:featureId="SensorNotificationService"
-             android:label="@string/sensor_notification_service"/>
-    <!-- Feature Id for Twilight service. -->
-    <feature android:featureId="TwilightService" android:label="@string/twilight_service"/>
-
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 24a21eb..c0de693 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -78,6 +78,14 @@
                 android:layout_height="wrap_content"
                 android:visibility="gone">
             </TabWidget>
+            <View
+                android:id="@+id/resolver_tab_divider"
+                android:visibility="gone"
+                android:layout_alwaysShow="true"
+                android:layout_width="match_parent"
+                android:layout_height="1dp"
+                android:background="?attr/colorBackgroundFloating"
+                android:foreground="?attr/dividerVertical" />
             <FrameLayout
                 android:id="@android:id/tabcontent"
                 android:layout_width="match_parent"
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index b4e6286..4359b10 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -94,6 +94,13 @@
                 android:layout_height="wrap_content"
                 android:visibility="gone">
             </TabWidget>
+            <View
+                android:id="@+id/resolver_tab_divider"
+                android:visibility="gone"
+                android:layout_width="match_parent"
+                android:layout_height="1dp"
+                android:background="?attr/colorBackgroundFloating"
+                android:foreground="?attr/dividerVertical" />
             <FrameLayout
                 android:id="@android:id/tabcontent"
                 android:layout_width="match_parent"
@@ -102,10 +109,7 @@
                     android:id="@+id/profile_pager"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:divider="?attr/dividerVertical"
-                    android:footerDividersEnabled="false"
-                    android:headerDividersEnabled="false"
-                    android:dividerHeight="1dp"/>
+                    android:minHeight="@dimen/resolver_empty_state_height" />
             </FrameLayout>
         </LinearLayout>
     </TabHost>
diff --git a/core/res/res/layout/resolver_list_per_profile.xml b/core/res/res/layout/resolver_list_per_profile.xml
index d481eff..c4f9ed9 100644
--- a/core/res/res/layout/resolver_list_per_profile.xml
+++ b/core/res/res/layout/resolver_list_per_profile.xml
@@ -29,7 +29,6 @@
         android:elevation="@dimen/resolver_elevation"
         android:nestedScrollingEnabled="true"
         android:scrollbarStyle="outsideOverlay"
-        android:scrollIndicators="top|bottom"
         android:divider="@null"
         android:footerDividersEnabled="false"
         android:headerDividersEnabled="false"
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index b546738..72e8b0c 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -175,6 +175,14 @@
                 android:layout_height="wrap_content"
                 android:visibility="gone">
             </TabWidget>
+            <View
+                android:id="@+id/resolver_tab_divider"
+                android:visibility="gone"
+                android:layout_alwaysShow="true"
+                android:layout_width="match_parent"
+                android:layout_height="1dp"
+                android:background="?attr/colorBackgroundFloating"
+                android:foreground="?attr/dividerVertical" />
             <FrameLayout
                 android:id="@android:id/tabcontent"
                 android:layout_width="match_parent"
@@ -183,10 +191,7 @@
                     android:id="@+id/profile_pager"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:dividerHeight="1dp"
-                    android:divider="?attr/dividerVertical"
-                    android:footerDividersEnabled="false"
-                    android:headerDividersEnabled="false"/>
+                    android:minHeight="@dimen/resolver_empty_state_height" />
             </FrameLayout>
         </LinearLayout>
     </TabHost>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e7ad8eb..e6a93e5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -421,14 +421,6 @@
          [CHAR LIMIT=NONE] -->
     <string name="location_changed_notification_text">Tap to see your location settings.</string>
 
-    <!-- Feature Id for Country Detector. [CHAR LIMIT=NONE]-->
-    <string name="country_detector">Country Detector</string>
-    <!-- Feature Id for Location service. [CHAR LIMIT=NONE]-->
-    <string name="location_service">Location Service</string>
-    <!-- Feature Id for Sensor Notification service. [CHAR LIMIT=NONE]-->
-    <string name="sensor_notification_service">Sensor Notification Service</string>
-    <!-- Feature Id for Twilight service. [CHAR LIMIT=NONE]-->
-    <string name="twilight_service">Twilight Service</string>
 
     <!-- Factory reset warning dialog strings--> <skip />
     <!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b8dd418..85c2a2a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3874,6 +3874,7 @@
   <java-symbol type="id" name="resolver_empty_state_title" />
   <java-symbol type="id" name="resolver_empty_state_subtitle" />
   <java-symbol type="id" name="resolver_empty_state_button" />
+  <java-symbol type="id" name="resolver_tab_divider" />
   <java-symbol type="string" name="resolver_cant_share_with_work_apps" />
   <java-symbol type="string" name="resolver_cant_share_with_personal_apps" />
   <java-symbol type="string" name="resolver_cant_share_cross_profile_explanation" />
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index ea0a0fd..37e8d59 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -244,6 +244,28 @@
     }
 
     @Test
+    public void testGetThermalHeadroom() throws Exception {
+        float headroom = mPm.getThermalHeadroom(0);
+        // If the device doesn't support thermal headroom, return early
+        if (Float.isNaN(headroom)) {
+            return;
+        }
+        assertTrue("Expected non-negative headroom", headroom >= 0.0f);
+        assertTrue("Expected reasonably small headroom", headroom < 10.0f);
+
+        // Call again immediately to ensure rate limiting works
+        headroom = mPm.getThermalHeadroom(0);
+        assertTrue("Expected NaN because of rate limiting", Float.isNaN(headroom));
+
+        // Sleep for a second before attempting to call again so as to not get rate limited
+        Thread.sleep(1000);
+        headroom = mPm.getThermalHeadroom(5);
+        assertFalse("Expected data to still be available", Float.isNaN(headroom));
+        assertTrue("Expected non-negative headroom", headroom >= 0.0f);
+        assertTrue("Expected reasonably small headroom", headroom < 10.0f);
+    }
+
+    @Test
     public void testUserspaceRebootNotSupported_throwsUnsupportedOperationException() {
         // Can't use assumption framework with AndroidTestCase :(
         if (mPm.isRebootingUserspaceSupported()) {
diff --git a/core/tests/coretests/src/android/view/CutoutSpecificationTest.java b/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
index 1f831bb..b41f90c 100644
--- a/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
+++ b/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
@@ -69,6 +69,13 @@
             + "z\n"
             + "@right\n"
             + "@bind_right_cutout\n"
+            + "@bottom\n"
+            + "M 0,0\n"
+            + "h -24\n"
+            + "v -48\n"
+            + "h 48\n"
+            + "v 48\n"
+            + "z\n"
             + "@dp";
     private static final String CORNER_CUTOUT_SPECIFICATION = "M 0,0\n"
             + "h 1\n"
@@ -141,13 +148,66 @@
     }
 
     @Test
+    public void parse_withBindMarker_shouldHaveTopBound() {
+        CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
+        assertThat(cutoutSpecification.getTopBound()).isEqualTo(new Rect(0, 0, 168, 168));
+    }
+
+    @Test
     public void parse_withBindMarker_shouldHaveRightBound() {
         CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
         assertThat(cutoutSpecification.getRightBound()).isEqualTo(new Rect(912, 960, 1080, 1128));
     }
 
     @Test
-    public void parse_tallCutout_shouldBeDone() {
+    public void parse_withBindMarker_shouldHaveBottomBound() {
+        CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
+        assertThat(cutoutSpecification.getBottomBound()).isEqualTo(new Rect(456, 1752, 624, 1920));
+    }
+
+    @Test
+    public void parse_withBindMarker_shouldMatchExpectedSafeInset() {
+        CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
+        assertThat(cutoutSpecification.getSafeInset()).isEqualTo(new Rect(168, 168, 168, 168));
+    }
+
+    @Test
+    public void parse_withBindMarker_tabletLikeDevice_shouldHaveLeftBound() {
+        CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+                .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+        assertThat(cutoutSpecification.getLeftBound()).isEqualTo(new Rect(0, 540, 168, 708));
+    }
+
+    @Test
+    public void parse_withBindMarker_tabletLikeDevice_shouldHaveTopBound() {
+        CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+                .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+        assertThat(cutoutSpecification.getTopBound()).isEqualTo(new Rect(0, 0, 168, 168));
+    }
+
+    @Test
+    public void parse_withBindMarker_tabletLikeDevice_shouldHaveRightBound() {
+        CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+                .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+        assertThat(cutoutSpecification.getRightBound()).isEqualTo(new Rect(1752, 540, 1920, 708));
+    }
+
+    @Test
+    public void parse_withBindMarker_tabletLikeDevice_shouldHaveBottomBound() {
+        CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+                .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+        assertThat(cutoutSpecification.getBottomBound()).isEqualTo(new Rect(876, 912, 1044, 1080));
+    }
+
+    @Test
+    public void parse_withBindMarker_tabletLikeDevice_shouldMatchExpectedSafeInset() {
+        CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+                .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+        assertThat(cutoutSpecification.getSafeInset()).isEqualTo(new Rect(168, 0, 168, 168));
+    }
+
+    @Test
+    public void parse_tallCutout_topBoundShouldMatchExpectedHeight() {
         CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
                 + "L -48, 0\n"
                 + "L -44.3940446283, 36.0595537175\n"
@@ -162,7 +222,7 @@
     }
 
     @Test
-    public void parse_wideCutout_shouldBeDone() {
+    public void parse_wideCutout_topBoundShouldMatchExpectedWidth() {
         CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
                 + "L -72, 0\n"
                 + "L -69.9940446283, 20.0595537175\n"
@@ -177,7 +237,7 @@
     }
 
     @Test
-    public void parse_narrowCutout_shouldBeDone() {
+    public void parse_narrowCutout_topBoundShouldHaveExpectedWidth() {
         CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
                 + "L -24, 0\n"
                 + "L -21.9940446283, 20.0595537175\n"
@@ -192,7 +252,7 @@
     }
 
     @Test
-    public void parse_doubleCutout_shouldBeDone() {
+    public void parse_doubleCutout_topBoundShouldHaveExpectedHeight() {
         CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
                 + "L -72, 0\n"
                 + "L -69.9940446283, 20.0595537175\n"
@@ -217,7 +277,7 @@
     }
 
     @Test
-    public void parse_cornerCutout_shouldBeDone() {
+    public void parse_cornerCutout_topBoundShouldHaveExpectedHeight() {
         CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
                 + "L -48, 0\n"
                 + "C -48,48 -48,48 0,48\n"
@@ -229,7 +289,7 @@
     }
 
     @Test
-    public void parse_holeCutout_shouldBeDone() {
+    public void parse_holeCutout_shouldMatchExpectedInset() {
         CutoutSpecification cutoutSpecification = mParser.parse("M 20.0,20.0\n"
                 + "h 136\n"
                 + "v 136\n"
@@ -259,4 +319,38 @@
         assertThat(cutoutSpecification.getSafeInset())
                 .isEqualTo(new Rect(6, 0, 8, 0));
     }
+
+    @Test
+    public void parse_bottomLeftSpec_withBindLeftMarker_shouldBeLeftBound() {
+        CutoutSpecification cutoutSpecification =
+                new CutoutSpecification.Parser(2f, 400, 200)
+                        .parse("@bottom"
+                                + "M 0,0\n"
+                                + "v -10\n"
+                                + "h 10\n"
+                                + "v 10\n"
+                                + "z\n"
+                                + "@left\n"
+                                + "@bind_left_cutout");
+
+        assertThat(cutoutSpecification.getLeftBound())
+                .isEqualTo(new Rect(0, 190, 10, 200));
+    }
+
+    @Test
+    public void parse_bottomRightSpec_withBindRightMarker_shouldBeRightBound() {
+        CutoutSpecification cutoutSpecification =
+                new CutoutSpecification.Parser(2f, 400, 200)
+                        .parse("@bottom"
+                                + "M 0,0\n"
+                                + "v -10\n"
+                                + "h 10\n"
+                                + "v 10\n"
+                                + "z\n"
+                                + "@right\n"
+                                + "@bind_right_cutout");
+
+        assertThat(cutoutSpecification.getRightBound())
+                .isEqualTo(new Rect(390, 190, 400, 200));
+    }
 }
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index ade1e0d..79e7c50 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -46,7 +46,7 @@
     // The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest:
     // See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo,
     // and assertAccessibilityNodeInfoCleared in that class.
-    private static final int NUM_MARSHALLED_PROPERTIES = 38;
+    private static final int NUM_MARSHALLED_PROPERTIES = 39;
 
     /**
      * The number of properties that are purposely not marshalled
diff --git a/libs/hwui/protos/graphicsstats.proto b/libs/hwui/protos/graphicsstats.proto
index dd5676c..745393c 100644
--- a/libs/hwui/protos/graphicsstats.proto
+++ b/libs/hwui/protos/graphicsstats.proto
@@ -30,8 +30,9 @@
 
 message GraphicsStatsProto {
     enum PipelineType {
-        GL = 0;
-        VULKAN = 1;
+        UNKNOWN = 0;
+        GL = 1;
+        VULKAN = 2;
     }
 
     // The package name of the app
diff --git a/packages/SystemUI/res/layout/qs_carrier.xml b/packages/SystemUI/res/layout/qs_carrier.xml
index 28b2d21..a5b8cfa 100644
--- a/packages/SystemUI/res/layout/qs_carrier.xml
+++ b/packages/SystemUI/res/layout/qs_carrier.xml
@@ -14,7 +14,7 @@
   ~ limitations under the License
   -->
 
-<com.android.systemui.qs.QSCarrier
+<com.android.systemui.qs.carrier.QSCarrier
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/linear_carrier"
     android:layout_width="wrap_content"
@@ -46,4 +46,4 @@
         android:singleLine="true"
         android:maxEms="7"/>
 
-</com.android.systemui.qs.QSCarrier>
\ No newline at end of file
+</com.android.systemui.qs.carrier.QSCarrier>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_carrier_group.xml b/packages/SystemUI/res/layout/qs_carrier_group.xml
index f2b0606..fd53a8b 100644
--- a/packages/SystemUI/res/layout/qs_carrier_group.xml
+++ b/packages/SystemUI/res/layout/qs_carrier_group.xml
@@ -15,7 +15,7 @@
   -->
 
 <!-- Extends LinearLayout -->
-<com.android.systemui.qs.QSCarrierGroup
+<com.android.systemui.qs.carrier.QSCarrierGroup
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/qs_mobile"
     android:layout_width="0dp"
@@ -71,4 +71,4 @@
         android:layout_weight="1"
         android:visibility="gone"/>
 
-</com.android.systemui.qs.QSCarrierGroup>
\ No newline at end of file
+</com.android.systemui.qs.carrier.QSCarrierGroup>
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 019cb14..17aaff1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -25,10 +25,13 @@
 import android.content.res.Configuration;
 import android.graphics.drawable.Animatable;
 import android.util.AttributeSet;
+import android.util.Pair;
 import android.util.SparseArray;
+import android.view.DisplayCutout;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
+import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -42,6 +45,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.StatusBarWindowView;
 
 public class QSDetail extends LinearLayout {
 
@@ -274,6 +278,32 @@
         }
     }
 
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        DisplayCutout cutout = insets.getDisplayCutout();
+
+        Pair<Integer, Integer> padding = StatusBarWindowView.cornerCutoutMargins(
+                cutout, getDisplay());
+
+        if (padding == null) {
+            mQsDetailHeader.setPaddingRelative(
+                    getResources().getDimensionPixelSize(R.dimen.qs_detail_header_padding),
+                    getPaddingTop(),
+                    getResources().getDimensionPixelSize(R.dimen.qs_detail_header_padding),
+                    getPaddingBottom()
+            );
+        } else {
+            mQsDetailHeader.setPadding(
+                    padding.first,
+                    getPaddingTop(),
+                    padding.second,
+                    getPaddingBottom()
+            );
+        }
+
+        return super.onApplyWindowInsets(insets);
+    }
+
     private void handleToggleStateChanged(boolean state, boolean toggleEnabled) {
         mSwitchState = state;
         if (mAnimatingOpen) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 8cd70cf..d422dd7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -60,6 +60,7 @@
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.qs.QSDetail.Callback;
+import com.android.systemui.qs.carrier.QSCarrierGroup;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
@@ -435,23 +436,22 @@
 
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        // Handle padding of SystemIconsView
         DisplayCutout cutout = insets.getDisplayCutout();
-
-        // Handle padding of QuickStatusBarHeader
         Pair<Integer, Integer> cornerCutoutPadding = StatusBarWindowView.cornerCutoutMargins(
                 cutout, getDisplay());
         Pair<Integer, Integer> padding =
                 StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
                         cutout, cornerCutoutPadding, mRoundedCornerPadding);
-        setPadding(padding.first, 0, padding.second, getPaddingBottom());
-
-        // Handle padding of SystemIconsView
         final int waterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
-        mSystemIconsView.setPaddingRelative(
-                getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start),
-                waterfallTopInset,
-                getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end),
-                0);
+        int statusBarPaddingLeft = isLayoutRtl()
+                ? getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end)
+                : getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start);
+        int statusBarPaddingRight = isLayoutRtl()
+                ? getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start)
+                : getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end);
+        mSystemIconsView.setPadding(padding.first + statusBarPaddingLeft, waterfallTopInset,
+                padding.second + statusBarPaddingRight, 0);
 
         return super.onApplyWindowInsets(insets);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index 867677a..d899acb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -17,6 +17,7 @@
 package com.android.systemui.qs;
 
 import com.android.systemui.R;
+import com.android.systemui.qs.carrier.QSCarrierGroupController;
 
 import javax.inject.Inject;
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt b/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
new file mode 100644
index 0000000..663f3f0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.qs.carrier
+
+/**
+ * Represents the state of cell signal for a particular slot.
+ *
+ * To be used between [QSCarrierGroupController] and [QSCarrier].
+ */
+data class CellSignalState(
+    @JvmField val visible: Boolean = false,
+    @JvmField val mobileSignalIconId: Int = 0,
+    @JvmField val contentDescription: String? = null,
+    @JvmField val typeContentDescription: String? = null,
+    @JvmField val roaming: Boolean = false
+) {
+    /**
+     * Changes the visibility of this state by returning a copy with the visibility changed.
+     *
+     * If the visibility would not change, the same state is returned.
+     *
+     * @param visible the new visibility state
+     * @return `this` if `this.visible == visible`. Else, a new copy with the visibility changed.
+     */
+    fun changeVisibility(visible: Boolean): CellSignalState {
+        if (this.visible == visible) return this
+        else return copy(visible = visible)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java
rename to packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
index 5a9c360..ad275f1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs;
+package com.android.systemui.qs.carrier;
 
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -29,6 +29,7 @@
 import com.android.settingslib.graph.SignalDrawable;
 import com.android.systemui.DualToneHandler;
 import com.android.systemui.R;
+import com.android.systemui.qs.QuickStatusBarHeader;
 
 import java.util.Objects;
 
@@ -41,7 +42,7 @@
     private DualToneHandler mDualToneHandler;
     private ColorStateList mColorForegroundStateList;
     private float mColorForegroundIntensity;
-    private QSCarrierGroupController.CellSignalState mLastSignalState;
+    private CellSignalState mLastSignalState;
 
     public QSCarrier(Context context) {
         super(context);
@@ -76,8 +77,13 @@
         mColorForegroundIntensity = QuickStatusBarHeader.getColorIntensity(colorForeground);
     }
 
-    public void updateState(QSCarrierGroupController.CellSignalState state) {
-        if (Objects.equals(state, mLastSignalState)) return;
+    /**
+     * Update the state of this view
+     * @param state the current state of the signal for this view
+     * @return true if the state was actually changed
+     */
+    public boolean updateState(CellSignalState state) {
+        if (Objects.equals(state, mLastSignalState)) return false;
         mLastSignalState = state;
         mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE);
         if (state.visible) {
@@ -103,6 +109,7 @@
             }
             mMobileSignal.setContentDescription(contentDescription);
         }
+        return true;
     }
 
     private boolean hasValidTypeContentDescription(String typeContentDescription) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroup.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
rename to packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroup.java
index 346c75d..d03563f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroup.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs;
+package com.android.systemui.qs.carrier;
 
 import android.content.Context;
 import android.util.AttributeSet;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java
rename to packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index eb5b4cc..f9b1473 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs;
+package com.android.systemui.qs.carrier;
 
 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
 
@@ -29,7 +29,6 @@
 import android.view.View;
 import android.widget.TextView;
 
-import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.keyguard.CarrierTextController;
@@ -38,7 +37,6 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.policy.NetworkController;
 
-import java.util.Objects;
 import java.util.function.Consumer;
 
 import javax.inject.Inject;
@@ -82,11 +80,13 @@
                         Log.e(TAG, "Invalid SIM slot index for subscription: " + subId);
                         return;
                     }
-                    mInfos[slotIndex].visible = statusIcon.visible;
-                    mInfos[slotIndex].mobileSignalIconId = statusIcon.icon;
-                    mInfos[slotIndex].contentDescription = statusIcon.contentDescription;
-                    mInfos[slotIndex].typeContentDescription = typeContentDescription.toString();
-                    mInfos[slotIndex].roaming = roaming;
+                    mInfos[slotIndex] = new CellSignalState(
+                            statusIcon.visible,
+                            statusIcon.icon,
+                            statusIcon.contentDescription,
+                            typeContentDescription.toString(),
+                            roaming
+                    );
                     mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
                 }
 
@@ -94,7 +94,7 @@
                 public void setNoSims(boolean hasNoSims, boolean simDetected) {
                     if (hasNoSims) {
                         for (int i = 0; i < SIM_SLOTS; i++) {
-                            mInfos[i].visible = false;
+                            mInfos[i] = mInfos[i].changeVisibility(false);
                         }
                     }
                     mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
@@ -236,7 +236,7 @@
                                         + info.subscriptionIds[i]);
                         continue;
                     }
-                    mInfos[slot].visible = true;
+                    mInfos[slot] = mInfos[slot].changeVisibility(true);
                     slotSeen[slot] = true;
                     mCarrierGroups[slot].setCarrierText(
                             info.listOfCarriers[i].toString().trim());
@@ -244,7 +244,7 @@
                 }
                 for (int i = 0; i < SIM_SLOTS; i++) {
                     if (!slotSeen[i]) {
-                        mInfos[i].visible = false;
+                        mInfos[i] = mInfos[i].changeVisibility(false);
                         mCarrierGroups[i].setVisibility(View.GONE);
                     }
                 }
@@ -255,7 +255,7 @@
             // No sims or airplane mode (but not WFC). Do not show QSCarrierGroup, instead just show
             // info.carrierText in a different view.
             for (int i = 0; i < SIM_SLOTS; i++) {
-                mInfos[i].visible = false;
+                mInfos[i] = mInfos[i].changeVisibility(false);
                 mCarrierGroups[i].setCarrierText("");
                 mCarrierGroups[i].setVisibility(View.GONE);
             }
@@ -295,35 +295,6 @@
         }
     }
 
-    static final class CellSignalState {
-        boolean visible;
-        int mobileSignalIconId;
-        String contentDescription;
-        String typeContentDescription;
-        boolean roaming;
-
-        @Override
-        public boolean equals(@Nullable Object obj) {
-            if (this == obj) return true;
-            if (!(obj instanceof CellSignalState)) return false;
-            CellSignalState other = (CellSignalState) obj;
-            return this.visible == other.visible
-                    && this.mobileSignalIconId == other.mobileSignalIconId
-                    && Objects.equals(this.contentDescription, other.contentDescription)
-                    && Objects.equals(this.typeContentDescription, other.typeContentDescription)
-                    && this.roaming == other.roaming;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(visible,
-                    mobileSignalIconId,
-                    contentDescription,
-                    typeContentDescription,
-                    roaming);
-        }
-    }
-
     public static class Builder {
         private QSCarrierGroup mView;
         private final ActivityStarter mActivityStarter;
@@ -343,7 +314,7 @@
             mCarrierTextControllerBuilder = carrierTextControllerBuilder;
         }
 
-        Builder setQSCarrierGroup(QSCarrierGroup view) {
+        public Builder setQSCarrierGroup(QSCarrierGroup view) {
             mView = view;
             return this;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index 2981252..e612c07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.statusbar.phone.NotificationGroupManager
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import dagger.Lazy
+import java.util.Comparator
 import java.util.Objects
 import javax.inject.Inject
 
@@ -73,6 +74,9 @@
         val aIsPeople = a.isPeopleNotification()
         val bIsPeople = b.isPeopleNotification()
 
+        val aIsImportantPeople = a.isImportantPeopleNotification()
+        val bIsImportantPeople = b.isImportantPeopleNotification()
+
         val aMedia = isImportantMedia(a)
         val bMedia = isImportantMedia(b)
 
@@ -87,6 +91,8 @@
 
         when {
             usePeopleFiltering && aIsPeople != bIsPeople -> if (aIsPeople) -1 else 1
+            usePeopleFiltering && aIsImportantPeople != bIsImportantPeople ->
+                if (aIsImportantPeople) -1 else 1
             aHeadsUp != bHeadsUp -> if (aHeadsUp) -1 else 1
             // Provide consistent ranking with headsUpManager
             aHeadsUp -> headsUpManager.compare(a, b)
@@ -192,6 +198,9 @@
     private fun NotificationEntry.isPeopleNotification() =
             peopleNotificationIdentifier.isPeopleNotification(sbn, ranking)
 
+    private fun NotificationEntry.isImportantPeopleNotification() =
+            peopleNotificationIdentifier.isImportantPeopleNotification(sbn, ranking)
+
     private fun NotificationEntry.isHighPriority() =
             highPriorityProvider.isHighPriority(this)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
index 4672de0..e15fa2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
@@ -23,6 +23,7 @@
 
 interface PeopleNotificationIdentifier {
     fun isPeopleNotification(sbn: StatusBarNotification, ranking: Ranking): Boolean
+    fun isImportantPeopleNotification(sbn: StatusBarNotification, ranking: Ranking): Boolean
 }
 
 @Singleton
@@ -32,4 +33,7 @@
 
     override fun isPeopleNotification(sbn: StatusBarNotification, ranking: Ranking) =
             ranking.isConversation || personExtractor.isPersonNotification(sbn)
+
+    override fun isImportantPeopleNotification(sbn: StatusBarNotification, ranking: Ranking) =
+            isPeopleNotification(sbn, ranking) && ranking.channel.isImportantConversation
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 55173182..234ab93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -1197,6 +1197,9 @@
         if (mMenuRow != null && mMenuRow.getMenuView() != null) {
             mMenuRow.onConfigurationChanged();
         }
+        if (mImageResolver != null) {
+            mImageResolver.updateMaxImageSizes();
+        }
     }
 
     public void onUiModeChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 248e5fe..60eda06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -22,6 +22,7 @@
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
+import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
@@ -112,7 +113,8 @@
     boolean mSkipPost = false;
 
     @Retention(SOURCE)
-    @IntDef({ACTION_BUBBLE, ACTION_HOME, ACTION_FAVORITE, ACTION_SNOOZE, ACTION_MUTE})
+    @IntDef({ACTION_BUBBLE, ACTION_HOME, ACTION_FAVORITE, ACTION_SNOOZE, ACTION_MUTE,
+            ACTION_UNBUBBLE, ACTION_SETTINGS})
     private @interface Action {}
     static final int ACTION_BUBBLE = 0;
     static final int ACTION_HOME = 1;
@@ -128,8 +130,6 @@
             mBubbleController.onUserDemotedBubbleFromNotification(mEntry);
         } else {
             mBubbleController.onUserCreatedBubbleFromNotification(mEntry);
-            Settings.Global.putInt(
-                    mContext.getContentResolver(), Settings.Global.NOTIFICATION_BUBBLES, 1);
         }
         closeControls(v, true);
     };
@@ -225,7 +225,9 @@
             mShortcutInfo = shortcuts.get(0);
         }
 
-        mIsBubbleable = mEntry.getBubbleMetadata() != null;
+        mIsBubbleable = mEntry.getBubbleMetadata() != null
+            && Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.NOTIFICATION_BUBBLES, 0) == 1;
         mStartedAsBubble = mEntry.isBubble();
 
         createConversationChannelIfNeeded();
@@ -382,6 +384,11 @@
         ((TextView) findViewById(R.id.pkg_name)).setText(mAppName);
     }
 
+    private boolean bubbleImportantConversations() {
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                BUBBLE_IMPORTANT_CONVERSATIONS, 1) == 1;
+    }
+
     private void bindDelegate() {
         TextView delegateView = findViewById(R.id.delegate_name);
 
@@ -579,6 +586,10 @@
                     case ACTION_FAVORITE:
                         mChannelToUpdate.setImportantConversation(
                                 !mChannelToUpdate.isImportantConversation());
+                        if (mChannelToUpdate.isImportantConversation()
+                                && bubbleImportantConversations()) {
+                            mChannelToUpdate.setAllowBubbles(true);
+                        }
                         break;
                     case ACTION_MUTE:
                         if (mChannelToUpdate.getImportance() == IMPORTANCE_UNSPECIFIED
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
index fa4bc2a..52f7c2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
@@ -19,12 +19,17 @@
 import android.app.ActivityManager;
 import android.app.Notification;
 import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.util.Log;
 
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.ImageResolver;
 import com.android.internal.widget.LocalImageResolver;
 import com.android.internal.widget.MessagingMessage;
@@ -36,6 +41,10 @@
 
 /**
  * Custom resolver with built-in image cache for image messages.
+ *
+ * If the URL points to a bitmap that's larger than the maximum width or height, the bitmap
+ * will be resized down to that maximum size before being cached. See {@link #getMaxImageWidth()},
+ * {@link #getMaxImageHeight()}, and {@link #resolveImage(Uri)} for the downscaling implementation.
  */
 public class NotificationInlineImageResolver implements ImageResolver {
     private static final String TAG = NotificationInlineImageResolver.class.getSimpleName();
@@ -44,6 +53,13 @@
     private final ImageCache mImageCache;
     private Set<Uri> mWantedUriSet;
 
+    // max allowed bitmap width, in pixels
+    @VisibleForTesting
+    protected int mMaxImageWidth;
+    // max allowed bitmap height, in pixels
+    @VisibleForTesting
+    protected int mMaxImageHeight;
+
     /**
      * Constructor.
      * @param context    Context.
@@ -56,6 +72,8 @@
         if (mImageCache != null) {
             mImageCache.setImageResolver(this);
         }
+
+        updateMaxImageSizes();
     }
 
     /**
@@ -66,14 +84,49 @@
         return mImageCache != null && !ActivityManager.isLowRamDeviceStatic();
     }
 
+    private boolean isLowRam() {
+        return ActivityManager.isLowRamDeviceStatic();
+    }
+
     /**
-     * To resolve image from specified uri directly.
+     * Update the maximum width and height allowed for bitmaps, ex. after a configuration change.
+     */
+    public void updateMaxImageSizes() {
+        mMaxImageWidth = getMaxImageWidth();
+        mMaxImageHeight = getMaxImageHeight();
+    }
+
+    @VisibleForTesting
+    protected int getMaxImageWidth() {
+        return mContext.getResources().getDimensionPixelSize(isLowRam()
+                ? R.dimen.notification_custom_view_max_image_width_low_ram
+                : R.dimen.notification_custom_view_max_image_width);
+    }
+
+    @VisibleForTesting
+    protected int getMaxImageHeight() {
+        return mContext.getResources().getDimensionPixelSize(isLowRam()
+                ? R.dimen.notification_custom_view_max_image_height_low_ram
+                : R.dimen.notification_custom_view_max_image_height);
+    }
+
+    @VisibleForTesting
+    protected BitmapDrawable resolveImageInternal(Uri uri) throws IOException {
+        return (BitmapDrawable) LocalImageResolver.resolveImage(uri, mContext);
+    }
+
+    /**
+     * To resolve image from specified uri directly. If the resulting image is larger than the
+     * maximum allowed size, scale it down.
      * @param uri Uri of the image.
      * @return Drawable of the image.
      * @throws IOException Throws if failed at resolving the image.
      */
     Drawable resolveImage(Uri uri) throws IOException {
-        return LocalImageResolver.resolveImage(uri, mContext);
+        BitmapDrawable image = resolveImageInternal(uri);
+        Bitmap bitmap = image.getBitmap();
+        image.setBitmap(Icon.scaleDownIfNecessary(bitmap, mMaxImageWidth, mMaxImageHeight));
+        return image;
     }
 
     @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/CellSignalStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/CellSignalStateTest.kt
new file mode 100644
index 0000000..75be74b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/CellSignalStateTest.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.qs.carrier
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert.assertNotSame
+import org.junit.Assert.assertSame
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class CellSignalStateTest : SysuiTestCase() {
+
+    @Test
+    fun testChangeVisibility_sameObject() {
+        val c = CellSignalState()
+
+        val other = c.changeVisibility(c.visible)
+
+        assertSame(c, other)
+    }
+
+    @Test
+    fun testChangeVisibility_otherObject() {
+        val c = CellSignalState()
+
+        val other = c.changeVisibility(!c.visible)
+
+        assertNotSame(c, other)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index 715087d..fa02231 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs;
+package com.android.systemui.qs.carrier;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
new file mode 100644
index 0000000..022dc84
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.qs.carrier;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class QSCarrierTest extends SysuiTestCase {
+
+    private QSCarrier mQSCarrier;
+    private TestableLooper mTestableLooper;
+
+    @Before
+    public void setUp() throws Exception {
+        mTestableLooper = TestableLooper.get(this);
+        LayoutInflater inflater = LayoutInflater.from(mContext);
+        mTestableLooper.runWithLooper(() ->
+                mQSCarrier = (QSCarrier) inflater.inflate(R.layout.qs_carrier, null));
+    }
+
+    @Test
+    public void testUpdateState_first() {
+        CellSignalState c = new CellSignalState(true, 0, "", "", false);
+
+        assertTrue(mQSCarrier.updateState(c));
+    }
+
+    @Test
+    public void testUpdateState_same() {
+        CellSignalState c = new CellSignalState(true, 0, "", "", false);
+
+        assertTrue(mQSCarrier.updateState(c));
+        assertFalse(mQSCarrier.updateState(c));
+    }
+
+    @Test
+    public void testUpdateState_changed() {
+        CellSignalState c = new CellSignalState(true, 0, "", "", false);
+
+        assertTrue(mQSCarrier.updateState(c));
+
+        CellSignalState other = c.changeVisibility(false);
+
+        assertTrue(mQSCarrier.updateState(other));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
index c6b496d..8e330c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -145,6 +145,47 @@
     }
 
     @Test
+    fun testSort_importantPeople() {
+        val aN = Notification.Builder(mContext, "test")
+                .setStyle(Notification.MessagingStyle(""))
+                .build()
+        val aC = NotificationChannel("test", "", IMPORTANCE_DEFAULT)
+        aC.setConversationId("parent", "convo")
+        val a = NotificationEntryBuilder()
+                .setImportance(IMPORTANCE_HIGH)
+                .setPkg("pkg")
+                .setOpPkg("pkg")
+                .setTag("tag")
+                .setNotification(aN)
+                .setChannel(aC)
+                .setUser(mContext.getUser())
+                .setOverrideGroupKey("")
+                .build()
+
+        val bN = Notification.Builder(mContext, "test")
+                .setStyle(Notification.MessagingStyle(""))
+                .build()
+        val bC = NotificationChannel("test", "", IMPORTANCE_DEFAULT)
+        bC.setConversationId("parent", "convo")
+        bC.setImportantConversation(true)
+        val b = NotificationEntryBuilder()
+                .setImportance(IMPORTANCE_HIGH)
+                .setPkg("pkg2")
+                .setOpPkg("pkg2")
+                .setTag("tag")
+                .setNotification(bN)
+                .setChannel(bC)
+                .setUser(mContext.getUser())
+                .setOverrideGroupKey("")
+                .build()
+
+
+        assertEquals(
+                listOf(b, a),
+                rankingManager.updateRanking(null, listOf(a, b), "test"))
+    }
+
+    @Test
     fun testSort_properlySetsAlertingBucket() {
         val notif = Notification.Builder(mContext, "test") .build()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 27b263f..138ea39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -21,6 +21,8 @@
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME;
+import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
+import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS;
 import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
 
@@ -55,6 +57,7 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Icon;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -449,6 +452,7 @@
 
     @Test
     public void testBindNotification_bubbleActionVisibleWhenCanBubble()  {
+        Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
         mNotificationInfo.bindNotification(
                 mShortcutManager,
                 mLauncherApps,
@@ -469,7 +473,8 @@
     }
 
     @Test
-    public void testBindNotification_bubbleActionVisibleWhenCannotBubble()  {
+    public void testBindNotification_bubbleAction_noBubbleMetadata()  {
+        Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
         mNotificationInfo.bindNotification(
                 mShortcutManager,
                 mLauncherApps,
@@ -490,6 +495,28 @@
     }
 
     @Test
+    public void testBindNotification_bubbleActionGloballyOff()  {
+        Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 0);
+        mNotificationInfo.bindNotification(
+                mShortcutManager,
+                mLauncherApps,
+                mMockPackageManager,
+                mMockINotificationManager,
+                mVisualStabilityManager,
+                TEST_PACKAGE_NAME,
+                mNotificationChannel,
+                mBubbleEntry,
+                null,
+                null,
+                null,
+                mIconFactory,
+                true);
+
+        View bubbleView = mNotificationInfo.findViewById(R.id.bubble);
+        assertEquals(View.GONE, bubbleView.getVisibility());
+    }
+
+    @Test
     public void testAddToHome() throws Exception {
         when(mShortcutManager.isRequestPinShortcutSupported()).thenReturn(true);
 
@@ -550,6 +577,7 @@
 
     @Test
     public void testBubble_promotesBubble() throws Exception {
+        Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
         mNotificationChannel.setAllowBubbles(false);
         mConversationChannel.setAllowBubbles(false);
 
@@ -584,6 +612,7 @@
 
     @Test
     public void testBubble_demotesBubble() throws Exception {
+        Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
         mBubbleEntry.getSbn().getNotification().flags |= FLAG_BUBBLE;
 
         mNotificationInfo.bindNotification(
@@ -617,6 +646,7 @@
 
     @Test
     public void testBubble_noChannelChange() throws Exception {
+        Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
         mNotificationInfo.bindNotification(
                 mShortcutManager,
                 mLauncherApps,
@@ -645,7 +675,11 @@
     }
 
     @Test
-    public void testFavorite_favorite() throws Exception {
+    public void testFavorite_favorite_noBubble() throws Exception {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                BUBBLE_IMPORTANT_CONVERSATIONS, 0);
+        mNotificationChannel.setAllowBubbles(false);
+        mConversationChannel.setAllowBubbles(false);
         mNotificationInfo.bindNotification(
                 mShortcutManager,
                 mLauncherApps,
@@ -673,6 +707,44 @@
         verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
                 anyString(), anyInt(), captor.capture());
         assertTrue(captor.getValue().isImportantConversation());
+        assertFalse(captor.getValue().canBubble());
+        verify(mBubbleController, never()).onUserCreatedBubbleFromNotification(mEntry);
+    }
+
+    @Test
+    public void testFavorite_favorite_bubble() throws Exception {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                BUBBLE_IMPORTANT_CONVERSATIONS, 1);
+        mNotificationChannel.setAllowBubbles(false);
+        mConversationChannel.setAllowBubbles(false);
+        mNotificationInfo.bindNotification(
+                mShortcutManager,
+                mLauncherApps,
+                mMockPackageManager,
+                mMockINotificationManager,
+                mVisualStabilityManager,
+                TEST_PACKAGE_NAME,
+                mNotificationChannel,
+                mEntry,
+                null,
+                null,
+                null,
+                mIconFactory,
+                true);
+
+        ImageButton fave = mNotificationInfo.findViewById(R.id.fave);
+        assertEquals(mContext.getString(R.string.notification_conversation_unfavorite),
+                fave.getContentDescription().toString());
+
+        fave.performClick();
+        mTestableLooper.processAllMessages();
+
+        ArgumentCaptor<NotificationChannel> captor =
+                ArgumentCaptor.forClass(NotificationChannel.class);
+        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+                anyString(), anyInt(), captor.capture());
+        assertTrue(captor.getValue().isImportantConversation());
+        assertTrue(captor.getValue().canBubble());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java
new file mode 100644
index 0000000..7f48cd1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.notification.row;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.net.Uri;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationInlineImageResolverTest extends SysuiTestCase {
+
+    NotificationInlineImageResolver mResolver;
+    Bitmap mBitmap;
+    BitmapDrawable mBitmapDrawable;
+    Uri mUri;
+
+    @Before
+    public void setup() {
+        mResolver = spy(new NotificationInlineImageResolver(mContext, null));
+        mBitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
+        mBitmapDrawable = new BitmapDrawable(mContext.getResources(), mBitmap);
+        mUri = mock(Uri.class);
+    }
+
+    @Test
+    public void refreshMaxImageSizes() {
+        assertNotEquals("Starts different height", mResolver.mMaxImageHeight, 20);
+        assertNotEquals("Starts different width", mResolver.mMaxImageWidth, 15);
+
+        doReturn(20).when(mResolver).getMaxImageHeight();
+        doReturn(15).when(mResolver).getMaxImageWidth();
+
+        mResolver.updateMaxImageSizes();
+
+        assertEquals("Height matches new config", mResolver.mMaxImageHeight, 20);
+        assertEquals("Width matches new config", mResolver.mMaxImageWidth, 15);
+    }
+
+    @Test
+    public void resolveImage_sizeTooBig() throws IOException {
+        doReturn(mBitmapDrawable).when(mResolver).resolveImageInternal(mUri);
+        mResolver.mMaxImageHeight = 5;
+        mResolver.mMaxImageWidth = 5;
+
+        // original bitmap size is 10x10
+        BitmapDrawable resolved = (BitmapDrawable) mResolver.resolveImage(mUri);
+        Bitmap resolvedBitmap = resolved.getBitmap();
+        assertEquals("Bitmap width reduced", 5, resolvedBitmap.getWidth());
+        assertEquals("Bitmap height reduced", 5, resolvedBitmap.getHeight());
+        assertNotSame("Bitmap replaced", resolvedBitmap, mBitmap);
+    }
+
+    @Test
+    public void resolveImage_sizeOK() throws IOException {
+        doReturn(mBitmapDrawable).when(mResolver).resolveImageInternal(mUri);
+        mResolver.mMaxImageWidth = 15;
+        mResolver.mMaxImageHeight = 15;
+
+        // original bitmap size is 10x10
+        BitmapDrawable resolved = (BitmapDrawable) mResolver.resolveImage(mUri);
+        Bitmap resolvedBitmap = resolved.getBitmap();
+        assertEquals("Bitmap width unchanged", 10, resolvedBitmap.getWidth());
+        assertEquals("Bitmap height unchanged", 10, resolvedBitmap.getHeight());
+        assertSame("Bitmap not replaced", resolvedBitmap, mBitmap);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index e5ee439..d81b8c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -50,6 +50,7 @@
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IPowerManager;
+import android.os.IThermalService;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.RemoteException;
@@ -256,7 +257,8 @@
         mDependency.injectTestDependency(NotificationFilter.class, mNotificationFilter);
 
         IPowerManager powerManagerService = mock(IPowerManager.class);
-        mPowerManager = new PowerManager(mContext, powerManagerService,
+        IThermalService thermalService = mock(IThermalService.class);
+        mPowerManager = new PowerManager(mContext, powerManagerService, thermalService,
                 Handler.createAsync(Looper.myLooper()));
 
         mNotificationInterruptionStateProvider =
diff --git a/services/Android.bp b/services/Android.bp
index 416f448..c77e75d 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -127,6 +127,16 @@
             api_file: "api/current.txt",
             removed_api_file: "api/removed.txt",
         },
+        last_released: {
+            api_file: ":last-released-system-server-api",
+            removed_api_file: "api/removed.txt",
+            baseline_file: ":system-server-api-incompatibilities-with-last-released"
+        },
+        api_lint: {
+            enabled: true,
+            new_since: ":last-released-system-server-api",
+            baseline_file: "api/lint-baseline.txt",
+        },
     },
 }
 
diff --git a/services/api/lint-baseline.txt b/services/api/lint-baseline.txt
new file mode 100644
index 0000000..0b8658c
--- /dev/null
+++ b/services/api/lint-baseline.txt
@@ -0,0 +1,35 @@
+// Baseline format: 1.0
+InternalClasses: com.android.permission.persistence.RuntimePermissionsPersistence:
+    Internal classes must not be exposed
+InternalClasses: com.android.permission.persistence.RuntimePermissionsState:
+    Internal classes must not be exposed
+InternalClasses: com.android.permission.persistence.RuntimePermissionsState.PermissionState:
+    Internal classes must not be exposed
+InternalClasses: com.android.role.persistence.RolesPersistence:
+    Internal classes must not be exposed
+InternalClasses: com.android.role.persistence.RolesState:
+    Internal classes must not be exposed
+InternalClasses: com.android.server.SystemService:
+    Internal classes must not be exposed
+InternalClasses: com.android.server.SystemService.TargetUser:
+    Internal classes must not be exposed
+
+
+ProtectedMember: com.android.server.SystemService#publishBinderService(String, android.os.IBinder):
+    Protected methods not allowed; must be public: method com.android.server.SystemService.publishBinderService(String,android.os.IBinder)}
+ProtectedMember: com.android.server.SystemService#publishBinderService(String, android.os.IBinder, boolean):
+    Protected methods not allowed; must be public: method com.android.server.SystemService.publishBinderService(String,android.os.IBinder,boolean)}
+
+
+UserHandleName: com.android.permission.persistence.RuntimePermissionsPersistence#delete(android.os.UserHandle):
+    Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `delete`
+UserHandleName: com.android.permission.persistence.RuntimePermissionsPersistence#read(android.os.UserHandle):
+    Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `read`
+UserHandleName: com.android.permission.persistence.RuntimePermissionsPersistence#write(com.android.permission.persistence.RuntimePermissionsState, android.os.UserHandle):
+    Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `write`
+UserHandleName: com.android.role.persistence.RolesPersistence#delete(android.os.UserHandle):
+    Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `delete`
+UserHandleName: com.android.role.persistence.RolesPersistence#read(android.os.UserHandle):
+    Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `read`
+UserHandleName: com.android.role.persistence.RolesPersistence#write(com.android.role.persistence.RolesState, android.os.UserHandle):
+    Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `write`
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7083281..f7eabac 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3430,16 +3430,16 @@
             // there is hope for it to become one if it validated, then it is needed.
             ensureRunningOnConnectivityServiceThread();
             if (nri.request.isRequest() && nai.satisfies(nri.request) &&
-                    (nai.isSatisfyingRequest(nri.request.requestId) ||
-                    // Note that this catches two important cases:
-                    // 1. Unvalidated cellular will not be reaped when unvalidated WiFi
-                    //    is currently satisfying the request.  This is desirable when
-                    //    cellular ends up validating but WiFi does not.
-                    // 2. Unvalidated WiFi will not be reaped when validated cellular
-                    //    is currently satisfying the request.  This is desirable when
-                    //    WiFi ends up validating and out scoring cellular.
-                    nri.mSatisfier.getCurrentScore()
-                            < nai.getCurrentScoreAsValidated())) {
+                    (nai.isSatisfyingRequest(nri.request.requestId)
+                    // Note that canPossiblyBeat catches two important cases:
+                    // 1. Unvalidated slow networks will not be reaped when an unvalidated fast
+                    // network is currently satisfying the request. This is desirable for example
+                    // when cellular ends up validating but WiFi/Ethernet does not.
+                    // 2. Fast networks will not be reaped when a validated slow network is
+                    // currently satisfying the request. This is desirable for example when
+                    // Ethernet ends up validating and out scoring WiFi, or WiFi/Ethernet ends
+                    // up validating and out scoring cellular.
+                            || nai.canPossiblyBeat(nri.mSatisfier))) {
                 return false;
             }
         }
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 207a6aa..5db5115 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -194,8 +194,6 @@
     // time
     private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100;
 
-    private static final String FEATURE_ID = "LocationService";
-
     private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
 
     private final Object mLock = new Object();
@@ -215,11 +213,6 @@
     private PackageManager mPackageManager;
     private PowerManager mPowerManager;
 
-    // TODO: sharing a location fudger with mock providers can leak information as the mock provider
-    //   can be used to retrieve offset information. the fudger should likely be reset whenever mock
-    //   providers are added or removed
-    private LocationFudger mLocationFudger;
-
     private GeofenceManager mGeofenceManager;
     private GeocoderProxy mGeocodeProvider;
 
@@ -245,7 +238,7 @@
     private int mBatterySaverMode;
 
     private LocationManagerService(Context context) {
-        mContext = context.createFeatureContext(FEATURE_ID);
+        mContext = context;
         mHandler = FgThread.getHandler();
         mLocalService = new LocalService();
 
@@ -287,8 +280,6 @@
             mPackageManager = mContext.getPackageManager();
             mAppOps = mContext.getSystemService(AppOpsManager.class);
             mPowerManager = mContext.getSystemService(PowerManager.class);
-
-            mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM());
             mGeofenceManager = new GeofenceManager(mContext, mSettingsHelper);
 
             PowerManagerInternal localPowerManager =
@@ -665,6 +656,8 @@
 
         private final String mName;
 
+        private final LocationFudger mLocationFudger;
+
         // if the provider is enabled for a given user id - null or not present means unknown
         @GuardedBy("mLock")
         private final SparseArray<Boolean> mEnabled;
@@ -682,6 +675,7 @@
 
         private LocationProviderManager(String name) {
             mName = name;
+            mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM());
             mEnabled = new SparseArray<>(2);
             mLastLocation = new SparseArray<>(2);
             mLastCoarseLocation = new SparseArray<>(2);
@@ -706,7 +700,9 @@
             synchronized (mLock) {
                 mProvider.setMockProvider(provider);
 
-                // when removing a mock provider, also clear any mock last locations
+                // when removing a mock provider, also clear any mock last locations and reset the
+                // location fudger. the mock provider could have been used to infer the current
+                // location fudger offsets.
                 if (provider == null) {
                     for (int i = 0; i < mLastLocation.size(); i++) {
                         Location lastLocation = mLastLocation.valueAt(i);
@@ -721,6 +717,8 @@
                             mLastCoarseLocation.setValueAt(i, null);
                         }
                     }
+
+                    mLocationFudger.resetOffsets();
                 }
             }
         }
diff --git a/services/core/java/com/android/server/SensorNotificationService.java b/services/core/java/com/android/server/SensorNotificationService.java
index 9082dca..7f5befa 100644
--- a/services/core/java/com/android/server/SensorNotificationService.java
+++ b/services/core/java/com/android/server/SensorNotificationService.java
@@ -16,8 +16,10 @@
 
 package com.android.server;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.hardware.GeomagneticField;
 import android.hardware.Sensor;
 import android.hardware.SensorAdditionalInfo;
@@ -29,7 +31,9 @@
 import android.location.LocationManager;
 import android.os.Bundle;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.Slog;
 
 public class SensorNotificationService extends SystemService
@@ -48,8 +52,6 @@
 
     private static final long MILLIS_2010_1_1 = 1262358000000l;
 
-    private static final String FEATURE_ID = "SensorNotificationService";
-
     private Context mContext;
     private SensorManager mSensorManager;
     private LocationManager mLocationManager;
@@ -59,8 +61,8 @@
     private long mLocalGeomagneticFieldUpdateTime = -LOCATION_MIN_TIME;
 
     public SensorNotificationService(Context context) {
-        super(context.createFeatureContext(FEATURE_ID));
-        mContext = getContext();
+        super(context);
+        mContext = context;
     }
 
     public void onStart() {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 53dbb93..3b0a1a3 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -4409,6 +4409,25 @@
             }
         }
 
+        @Override
+        public void prepareAppDataAfterInstall(String packageName, int uid) {
+            int userId = UserHandle.getUserId(uid);
+            final Environment.UserEnvironment userEnv = new Environment.UserEnvironment(userId);
+
+            // The installer may have downloaded OBBs for this newly installed application;
+            // make sure the OBB dir for the application is setup correctly, if it exists.
+            File[] packageObbDirs = userEnv.buildExternalStorageAppObbDirs(packageName);
+            for (File packageObbDir : packageObbDirs) {
+                try {
+                    mVold.fixupAppDir(packageObbDir.getCanonicalPath() + "/", uid);
+                } catch (IOException e) {
+                    Log.e(TAG, "Failed to get canonical path for " + packageName);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to fixup app dir for " + packageName);
+                }
+            }
+        }
+
         public boolean hasExternalStorage(int uid, String packageName) {
             // No need to check for system uid. This avoids a deadlock between
             // PackageManagerService and AppOpsService.
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 430a5b9..50f43b5 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -49,6 +49,7 @@
 import android.app.PendingIntent;
 import android.app.Service;
 import android.app.ServiceStartArgs;
+import android.app.admin.DevicePolicyEventLogger;
 import android.appwidget.AppWidgetManagerInternal;
 import android.content.ComponentName;
 import android.content.ComponentName.WithComponentName;
@@ -79,6 +80,7 @@
 import android.os.TransactionTooLargeException;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.stats.devicepolicy.DevicePolicyEnums;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -1912,6 +1914,8 @@
                 requestServiceBindingLocked(s, b.intent, callerFg, false);
             }
 
+            maybeLogBindCrossProfileService(userId, callingPackage, callerApp.info.uid);
+
             getServiceMapLocked(s.userId).ensureNotStartingBackgroundLocked(s);
 
         } finally {
@@ -1921,6 +1925,21 @@
         return 1;
     }
 
+    private void maybeLogBindCrossProfileService(
+            int userId, String callingPackage, int callingUid) {
+        if (UserHandle.isCore(callingUid)) {
+            return;
+        }
+        final int callingUserId = UserHandle.getCallingUserId();
+        if (callingUserId == userId
+                || !mAm.mUserController.isSameProfileGroup(callingUserId, userId)) {
+            return;
+        }
+        DevicePolicyEventLogger.createEvent(DevicePolicyEnums.BIND_CROSS_PROFILE_SERVICE)
+                .setStrings(callingPackage)
+                .write();
+    }
+
     void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
         final long origId = Binder.clearCallingIdentity();
         try {
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 4612cfd..3860904 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -16,6 +16,8 @@
 
 package com.android.server.connectivity;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.transportNamesOf;
 
 import android.annotation.NonNull;
@@ -475,24 +477,16 @@
         return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
     }
 
-    private int getCurrentScore(boolean pretendValidated) {
-        // TODO: We may want to refactor this into a NetworkScore class that takes a base score from
-        // the NetworkAgent and signals from the NetworkAgent and uses those signals to modify the
-        // score.  The NetworkScore class would provide a nice place to centralize score constants
-        // so they are not scattered about the transports.
-
+    /** Gets the current score */
+    public int getCurrentScore() {
         // If this network is explicitly selected and the user has decided to use it even if it's
-        // unvalidated, give it the maximum score. Also give it the maximum score if it's explicitly
-        // selected and we're trying to see what its score could be. This ensures that we don't tear
-        // down an explicitly selected network before the user gets a chance to prefer it when
-        // a higher-scoring network (e.g., Ethernet) is available.
-        if (networkAgentConfig.explicitlySelected
-                && (networkAgentConfig.acceptUnvalidated || pretendValidated)) {
+        // unvalidated, give it the maximum score.
+        if (networkAgentConfig.explicitlySelected && networkAgentConfig.acceptUnvalidated) {
             return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE;
         }
 
         int score = mNetworkScore.getLegacyScore();
-        if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) {
+        if (!lastValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) {
             score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY;
         }
         if (score < 0) score = 0;
@@ -508,18 +502,6 @@
         return isWifi && !avoidBadWifi && everValidated;
     }
 
-    // Get the current score for this Network.  This may be modified from what the
-    // NetworkAgent sent, as it has modifiers applied to it.
-    public int getCurrentScore() {
-        return getCurrentScore(false);
-    }
-
-    // Get the current score for this Network as if it was validated.  This may be modified from
-    // what the NetworkAgent sent, as it has modifiers applied to it.
-    public int getCurrentScoreAsValidated() {
-        return getCurrentScore(true);
-    }
-
     public void setNetworkScore(@NonNull NetworkScore ns) {
         mNetworkScore = ns;
     }
@@ -629,6 +611,41 @@
         mLingering = false;
     }
 
+    /**
+     * Returns whether this NAI has any chance of ever beating this other agent.
+     *
+     * The chief use case of this is the decision to tear down this network. ConnectivityService
+     * tears down networks that don't satisfy any request, unless they have a chance to beat any
+     * existing satisfier.
+     *
+     * @param other the agent to beat
+     * @return whether this should be given more time to try and beat the other agent
+     * TODO : remove this and migrate to a ranker-based approach
+     */
+    public boolean canPossiblyBeat(@NonNull final NetworkAgentInfo other) {
+        // Any explicitly selected network should be held on.
+        if (networkAgentConfig.explicitlySelected) return true;
+        // An outscored exiting network should be torn down.
+        if (mNetworkScore.isExiting()) return false;
+        // If this network is validated it can be torn down as it can't hope to be better than
+        // it already is.
+        if (networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) return false;
+        // If neither network is validated, keep both until at least one does.
+        if (!other.networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) return true;
+        // If this network is not metered but the other is, it should be preferable if it validates.
+        if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)
+                && !other.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
+            return true;
+        }
+
+        // If the control comes here :
+        // • This network is neither exiting or explicitly selected
+        // • This network is not validated, but the other is
+        // • This network is metered, or both networks are unmetered
+        // Keep it if it's expected to be faster than the other., should it validate.
+        return mNetworkScore.probablyFasterThan(other.mNetworkScore);
+    }
+
     public void dumpLingerTimers(PrintWriter pw) {
         for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
     }
diff --git a/services/core/java/com/android/server/connectivity/NetworkRanker.java b/services/core/java/com/android/server/connectivity/NetworkRanker.java
index c536ab2..80d46e0 100644
--- a/services/core/java/com/android/server/connectivity/NetworkRanker.java
+++ b/services/core/java/com/android/server/connectivity/NetworkRanker.java
@@ -16,6 +16,9 @@
 
 package com.android.server.connectivity;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkScore.POLICY_IGNORE_ON_WIFI;
 
 import static com.android.internal.util.FunctionalUtils.findFirst;
@@ -42,13 +45,20 @@
             @NonNull final Collection<NetworkAgentInfo> nais) {
         final ArrayList<NetworkAgentInfo> candidates = new ArrayList<>(nais);
         candidates.removeIf(nai -> !nai.satisfies(request));
-        // Enforce policy.
-        filterBadWifiAvoidancePolicy(candidates);
+
+        // Enforce policy. The order in which the policy is computed is essential, because each
+        // step may remove some of the candidates. For example, filterValidated drops non-validated
+        // networks in presence of validated networks for INTERNET requests, but the bad wifi
+        // avoidance policy takes priority over this, so it must be done before.
+        filterVpn(candidates);
+        filterExplicitlySelected(candidates);
+        filterBadWifiAvoidance(candidates);
+        filterValidated(request, candidates);
 
         NetworkAgentInfo bestNetwork = null;
         int bestScore = Integer.MIN_VALUE;
         for (final NetworkAgentInfo nai : candidates) {
-            final int score = nai.getCurrentScore();
+            final int score = nai.getNetworkScore().getLegacyScore();
             if (score > bestScore) {
                 bestNetwork = nai;
                 bestScore = score;
@@ -57,9 +67,27 @@
         return bestNetwork;
     }
 
-    // If some network with wifi transport is present, drop all networks with POLICY_IGNORE_ON_WIFI.
-    private void filterBadWifiAvoidancePolicy(
+    // If a network is a VPN it has priority.
+    private void filterVpn(@NonNull final ArrayList<NetworkAgentInfo> candidates) {
+        final NetworkAgentInfo vpn = findFirst(candidates,
+                nai -> nai.networkCapabilities.hasTransport(TRANSPORT_VPN));
+        if (null == vpn) return; // No VPN : this policy doesn't apply.
+        candidates.removeIf(nai -> !nai.networkCapabilities.hasTransport(TRANSPORT_VPN));
+    }
+
+    // If some network is explicitly selected and set to accept unvalidated connectivity, then
+    // drop all networks that are not explicitly selected.
+    private void filterExplicitlySelected(
             @NonNull final ArrayList<NetworkAgentInfo> candidates) {
+        final NetworkAgentInfo explicitlySelected = findFirst(candidates,
+                nai -> nai.networkAgentConfig.explicitlySelected
+                        && nai.networkAgentConfig.acceptUnvalidated);
+        if (null == explicitlySelected) return; // No explicitly selected network accepting unvalid
+        candidates.removeIf(nai -> !nai.networkAgentConfig.explicitlySelected);
+    }
+
+    // If some network with wifi transport is present, drop all networks with POLICY_IGNORE_ON_WIFI.
+    private void filterBadWifiAvoidance(@NonNull final ArrayList<NetworkAgentInfo> candidates) {
         final NetworkAgentInfo wifi = findFirst(candidates,
                 nai -> nai.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
                         && nai.everValidated
@@ -71,4 +99,16 @@
         if (null == wifi) return; // No wifi : this policy doesn't apply
         candidates.removeIf(nai -> nai.getNetworkScore().hasPolicy(POLICY_IGNORE_ON_WIFI));
     }
+
+    // If some network is validated and the request asks for INTERNET, drop all networks that are
+    // not validated.
+    private void filterValidated(@NonNull final NetworkRequest request,
+            @NonNull final ArrayList<NetworkAgentInfo> candidates) {
+        if (!request.hasCapability(NET_CAPABILITY_INTERNET)) return;
+        final NetworkAgentInfo validated = findFirst(candidates,
+                nai -> nai.networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED));
+        if (null == validated) return; // No validated network
+        candidates.removeIf(nai ->
+                !nai.networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED));
+    }
 }
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index ac41434..18adc0b 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.server.display;
 
+import android.hardware.display.DeviceProductInfo;
 import android.hardware.display.DisplayViewport;
 import android.util.DisplayMetrics;
 import android.view.Display;
@@ -288,6 +289,13 @@
     public DisplayAddress address;
 
     /**
+     * Product-specific information about the display or the directly connected device on the
+     * display chain. For example, if the display is transitively connected, this field may contain
+     * product information about the intermediate device.
+     */
+    public DeviceProductInfo deviceProductInfo;
+
+    /**
      * Display state.
      */
     public int state = Display.STATE_ON;
@@ -360,6 +368,7 @@
                 || rotation != other.rotation
                 || type != other.type
                 || !Objects.equals(address, other.address)
+                || !Objects.equals(deviceProductInfo, other.deviceProductInfo)
                 || ownerUid != other.ownerUid
                 || !Objects.equals(ownerPackageName, other.ownerPackageName)) {
             diff |= DIFF_OTHER;
@@ -396,6 +405,7 @@
         rotation = other.rotation;
         type = other.type;
         address = other.address;
+        deviceProductInfo = other.deviceProductInfo;
         state = other.state;
         ownerUid = other.ownerUid;
         ownerPackageName = other.ownerPackageName;
@@ -429,6 +439,7 @@
         if (address != null) {
             sb.append(", address ").append(address);
         }
+        sb.append(", deviceProductInfo ").append(deviceProductInfo);
         sb.append(", state ").append(Display.stateToString(state));
         if (ownerUid != 0 || ownerPackageName != null) {
             sb.append(", owner ").append(ownerPackageName);
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 4ebbdda..e578ac1 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -513,6 +513,7 @@
                 mInfo.densityDpi = (int) (mDisplayInfo.density * 160 + 0.5f);
                 mInfo.xDpi = config.xDpi;
                 mInfo.yDpi = config.yDpi;
+                mInfo.deviceProductInfo = mDisplayInfo.deviceProductInfo;
 
                 // Assume that all built-in displays that have secure output (eg. HDCP) also
                 // support compositing from gralloc protected buffers.
@@ -891,8 +892,8 @@
             pw.println("mBacklight=" + mBacklight);
             pw.println("mAllmSupported=" + mAllmSupported);
             pw.println("mAllmRequested=" + mAllmRequested);
-            pw.println("mGameContentTypeSupported" + mGameContentTypeSupported);
-            pw.println("mGameContentTypeRequested" + mGameContentTypeRequested);
+            pw.println("mGameContentTypeSupported=" + mGameContentTypeSupported);
+            pw.println("mGameContentTypeRequested=" + mGameContentTypeRequested);
             pw.println("mDisplayInfo=" + mDisplayInfo);
             pw.println("mDisplayConfigs=");
             for (int i = 0; i < mDisplayConfigs.length; i++) {
@@ -902,14 +903,7 @@
             for (int i = 0; i < mSupportedModes.size(); i++) {
                 pw.println("  " + mSupportedModes.valueAt(i));
             }
-            pw.print("mSupportedColorModes=[");
-            for (int i = 0; i < mSupportedColorModes.size(); i++) {
-                if (i != 0) {
-                    pw.print(", ");
-                }
-                pw.print(mSupportedColorModes.get(i));
-            }
-            pw.println("]");
+            pw.print("mSupportedColorModes=" + mSupportedColorModes.toString());
         }
 
         private int findDisplayConfigIdLocked(int modeId) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 0c9445a..ac81a6c 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -269,6 +269,7 @@
 
             mBaseDisplayInfo.type = deviceInfo.type;
             mBaseDisplayInfo.address = deviceInfo.address;
+            mBaseDisplayInfo.deviceProductInfo = deviceInfo.deviceProductInfo;
             mBaseDisplayInfo.name = deviceInfo.name;
             mBaseDisplayInfo.uniqueId = deviceInfo.uniqueId;
             mBaseDisplayInfo.appWidth = maskedWidth;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 6174e54..b84d322 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -77,7 +77,7 @@
 
     private static final int NUM_LOGICAL_ADDRESS = 16;
 
-    private static final int MAX_CEC_MESSAGE_HISTORY = 200;
+    private static final int MAX_HDMI_MESSAGE_HISTORY = 250;
 
     // Predicate for whether the given logical address is remote device's one or not.
     private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
@@ -111,9 +111,9 @@
     // Stores the local CEC devices in the system. Device type is used for key.
     private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
 
-    // Stores recent CEC messages history for debugging purpose.
-    private final ArrayBlockingQueue<MessageHistoryRecord> mMessageHistory =
-            new ArrayBlockingQueue<>(MAX_CEC_MESSAGE_HISTORY);
+    // Stores recent CEC messages and HDMI Hotplug event history for debugging purpose.
+    private final ArrayBlockingQueue<Dumpable> mMessageHistory =
+            new ArrayBlockingQueue<>(MAX_HDMI_MESSAGE_HISTORY);
 
     private final NativeWrapper mNativeWrapperImpl;
 
@@ -618,7 +618,7 @@
     void sendCommand(final HdmiCecMessage cecMessage,
             final HdmiControlService.SendMessageCallback callback) {
         assertRunOnServiceThread();
-        addMessageToHistory(false /* isReceived */, cecMessage);
+        addCecMessageToHistory(false /* isReceived */, cecMessage);
         runOnIoThread(new Runnable() {
             @Override
             public void run() {
@@ -658,7 +658,7 @@
         assertRunOnServiceThread();
         HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
         HdmiLogger.debug("[R]:" + command);
-        addMessageToHistory(true /* isReceived */, command);
+        addCecMessageToHistory(true /* isReceived */, command);
         onReceiveCommand(command);
     }
 
@@ -669,16 +669,26 @@
     private void handleHotplug(int port, boolean connected) {
         assertRunOnServiceThread();
         HdmiLogger.debug("Hotplug event:[port:%d, connected:%b]", port, connected);
+        addHotplugEventToHistory(port, connected);
         mService.onHotplug(port, connected);
     }
 
     @ServiceThreadOnly
-    private void addMessageToHistory(boolean isReceived, HdmiCecMessage message) {
+    private void addHotplugEventToHistory(int port, boolean connected) {
         assertRunOnServiceThread();
-        MessageHistoryRecord record = new MessageHistoryRecord(isReceived, message);
-        if (!mMessageHistory.offer(record)) {
+        addEventToHistory(new HotplugHistoryRecord(port, connected));
+    }
+
+    @ServiceThreadOnly
+    private void addCecMessageToHistory(boolean isReceived, HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        addEventToHistory(new MessageHistoryRecord(isReceived, message));
+    }
+
+    private void addEventToHistory(Dumpable event) {
+        if (!mMessageHistory.offer(event)) {
             mMessageHistory.poll();
-            mMessageHistory.offer(record);
+            mMessageHistory.offer(event);
         }
     }
 
@@ -689,10 +699,11 @@
             mLocalDevices.valueAt(i).dump(pw);
             pw.decreaseIndent();
         }
+
         pw.println("CEC message history:");
         pw.increaseIndent();
         final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-        for (MessageHistoryRecord record : mMessageHistory) {
+        for (Dumpable record : mMessageHistory) {
             record.dump(pw, sdf);
         }
         pw.decreaseIndent();
@@ -792,17 +803,27 @@
         }
     }
 
-    private final class MessageHistoryRecord {
-        private final long mTime;
+    private abstract static class Dumpable {
+        protected final long mTime;
+
+        Dumpable() {
+            mTime = System.currentTimeMillis();
+        }
+
+        abstract void dump(IndentingPrintWriter pw, SimpleDateFormat sdf);
+    }
+
+    private static final class MessageHistoryRecord extends Dumpable {
         private final boolean mIsReceived; // true if received message and false if sent message
         private final HdmiCecMessage mMessage;
 
-        public MessageHistoryRecord(boolean isReceived, HdmiCecMessage message) {
-            mTime = System.currentTimeMillis();
+        MessageHistoryRecord(boolean isReceived, HdmiCecMessage message) {
+            super();
             mIsReceived = isReceived;
             mMessage = message;
         }
 
+        @Override
         void dump(final IndentingPrintWriter pw, SimpleDateFormat sdf) {
             pw.print(mIsReceived ? "[R]" : "[S]");
             pw.print(" time=");
@@ -811,4 +832,26 @@
             pw.println(mMessage);
         }
     }
+
+    private static final class HotplugHistoryRecord extends Dumpable {
+        private final int mPort;
+        private final boolean mConnected;
+
+        HotplugHistoryRecord(int port, boolean connected) {
+            super();
+            mPort = port;
+            mConnected = connected;
+        }
+
+        @Override
+        void dump(final IndentingPrintWriter pw, SimpleDateFormat sdf) {
+            pw.print("[H]");
+            pw.print(" time=");
+            pw.print(sdf.format(new Date(mTime)));
+            pw.print(" hotplug port=");
+            pw.print(mPort);
+            pw.print(" connected=");
+            pw.println(mConnected);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/location/CountryDetectorBase.java b/services/core/java/com/android/server/location/CountryDetectorBase.java
index b158388..8326ef9 100644
--- a/services/core/java/com/android/server/location/CountryDetectorBase.java
+++ b/services/core/java/com/android/server/location/CountryDetectorBase.java
@@ -31,15 +31,13 @@
  * @hide
  */
 public abstract class CountryDetectorBase {
-    private static final String FEATURE_ID = "CountryDetector";
-
     protected final Handler mHandler;
     protected final Context mContext;
     protected CountryListener mListener;
     protected Country mDetectedCountry;
 
-    public CountryDetectorBase(Context context) {
-        mContext = context.createFeatureContext(FEATURE_ID);
+    public CountryDetectorBase(Context ctx) {
+        mContext = ctx;
         mHandler = new Handler();
     }
 
@@ -47,7 +45,7 @@
      * Start detecting the country that the user is in.
      *
      * @return the country if it is available immediately, otherwise null should
-     * be returned.
+     *         be returned.
      */
     public abstract Country detectCountry();
 
diff --git a/services/core/java/com/android/server/location/LocationFudger.java b/services/core/java/com/android/server/location/LocationFudger.java
index a069e7a..1f458ed 100644
--- a/services/core/java/com/android/server/location/LocationFudger.java
+++ b/services/core/java/com/android/server/location/LocationFudger.java
@@ -87,6 +87,13 @@
         mRandom = random;
         mAccuracyM = Math.max(accuracyM, MIN_ACCURACY_M);
 
+        resetOffsets();
+    }
+
+    /**
+     * Resets the random offsets completely.
+     */
+    public void resetOffsets() {
         mLatitudeOffsetM = nextRandomOffset();
         mLongitudeOffsetM = nextRandomOffset();
         mNextUpdateRealtimeMs = mClock.millis() + OFFSET_UPDATE_INTERVAL_MS;
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 05867ba..f7e1398 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -214,6 +214,8 @@
      * Updates the mSessionInfo. Returns true if the session info is changed.
      */
     boolean updateSessionInfosIfNeededLocked() {
+        // Prevent to execute this method before mBtRouteProvider is created.
+        if (mBtRouteProvider == null) return false;
         RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(0);
 
         RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index fd86f1d..0d402e5 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3440,6 +3440,27 @@
         }
 
         @Override
+        public ParceledListSlice<ConversationChannelWrapper> getConversations(
+                boolean onlyImportant) {
+            enforceSystemOrSystemUI("getConversations");
+            ArrayList<ConversationChannelWrapper> conversations =
+                    mPreferencesHelper.getConversations(onlyImportant);
+            for (ConversationChannelWrapper conversation : conversations) {
+                LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
+                        .setPackage(conversation.getPkg())
+                        .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
+                        .setShortcutIds(Arrays.asList(
+                                conversation.getNotificationChannel().getConversationId()));
+                List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(
+                        query, UserHandle.of(UserHandle.getUserId(conversation.getUid())));
+                if (shortcuts != null && !shortcuts.isEmpty()) {
+                    conversation.setShortcutInfo(shortcuts.get(0));
+                }
+            }
+            return new ParceledListSlice<>(conversations);
+        }
+
+        @Override
         public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
                 String pkg, int uid, boolean includeDeleted) {
             enforceSystemOrSystemUI("getNotificationChannelGroupsForPackage");
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 20c8625..b8186ed 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1186,6 +1186,44 @@
         return groups;
     }
 
+    public ArrayList<ConversationChannelWrapper> getConversations(boolean onlyImportant) {
+        synchronized (mPackagePreferences) {
+            ArrayList<ConversationChannelWrapper> conversations = new ArrayList<>();
+
+            for (PackagePreferences p : mPackagePreferences.values()) {
+                int N = p.channels.size();
+                for (int i = 0; i < N; i++) {
+                    final NotificationChannel nc = p.channels.valueAt(i);
+                    if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted()
+                            && (nc.isImportantConversation() || !onlyImportant)) {
+                        ConversationChannelWrapper conversation = new ConversationChannelWrapper();
+                        conversation.setPkg(p.pkg);
+                        conversation.setUid(p.uid);
+                        conversation.setNotificationChannel(nc);
+                        conversation.setParentChannelLabel(
+                                p.channels.get(nc.getParentChannelId()).getName());
+                        boolean blockedByGroup = false;
+                        if (nc.getGroup() != null) {
+                            NotificationChannelGroup group = p.groups.get(nc.getGroup());
+                            if (group != null) {
+                                if (group.isBlocked()) {
+                                    blockedByGroup = true;
+                                } else {
+                                    conversation.setGroupLabel(group.getName());
+                                }
+                            }
+                        }
+                        if (!blockedByGroup) {
+                            conversations.add(conversation);
+                        }
+                    }
+                }
+            }
+
+            return conversations;
+        }
+    }
+
     public ArrayList<ConversationChannelWrapper> getConversations(String pkg, int uid) {
         Objects.requireNonNull(pkg);
         synchronized (mPackagePreferences) {
@@ -1199,6 +1237,8 @@
                 final NotificationChannel nc = r.channels.valueAt(i);
                 if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted()) {
                     ConversationChannelWrapper conversation = new ConversationChannelWrapper();
+                    conversation.setPkg(r.pkg);
+                    conversation.setUid(r.uid);
                     conversation.setNotificationChannel(nc);
                     conversation.setParentChannelLabel(
                             r.channels.get(nc.getParentChannelId()).getName());
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f6eb76b..2a3f7ed 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -15158,10 +15158,13 @@
 
                 // We purposefully exclude FLAG_STORAGE_EXTERNAL here, since
                 // this task was only focused on moving data on internal storage.
+                // We don't want ART profiles cleared, because they don't move,
+                // so we would be deleting the only copy (b/149200535).
+                final int flags = FLAG_STORAGE_DE | FLAG_STORAGE_CE
+                        | Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES;
                 for (int userId : userIds) {
                     try {
-                        mInstaller.destroyAppData(volumeUuid, move.packageName, userId,
-                                StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE, 0);
+                        mInstaller.destroyAppData(volumeUuid, move.packageName, userId, flags, 0);
                     } catch (InstallerException e) {
                         Slog.w(TAG, String.valueOf(e));
                     }
@@ -21733,6 +21736,7 @@
         }
 
         UserManagerInternal umInternal = mInjector.getUserManagerInternal();
+        StorageManagerInternal smInternal = mInjector.getStorageManagerInternal();
         for (UserInfo user : mUserManager.getUsers(false /*excludeDying*/)) {
             final int flags;
             if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
@@ -21746,6 +21750,13 @@
             if (ps.getInstalled(user.id)) {
                 // TODO: when user data is locked, mark that we're still dirty
                 prepareAppDataLIF(pkg, user.id, flags);
+
+                if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
+                    // Prepare app data on external storage; currently this is used to
+                    // setup any OBB dirs that were created by the installer correctly.
+                    int uid = UserHandle.getUid(user.id, UserHandle.getAppId(pkg.getUid()));
+                    smInternal.prepareAppDataAfterInstall(pkg.getPackageName(), uid);
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 377fd16..12f7d5c 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -2773,6 +2773,13 @@
                     userId, /* doCache= */ false);
         }
 
+        @Override
+        public List<ShortcutManager.ShareShortcutInfo> getShareTargets(
+                @NonNull String callingPackage, @NonNull IntentFilter intentFilter, int userId) {
+            return ShortcutService.this.getShareTargets(
+                    callingPackage, intentFilter, userId).getList();
+        }
+
         private void updateCachedShortcutsInternal(int launcherUserId,
                 @NonNull String callingPackage, @NonNull String packageName,
                 @NonNull List<String> shortcutIds, int userId, boolean doCache) {
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 491c5ab..da3cbf9 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -16,15 +16,18 @@
 
 package com.android.server.power;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.thermal.V1_0.ThermalStatus;
 import android.hardware.thermal.V1_0.ThermalStatusCode;
 import android.hardware.thermal.V1_1.IThermalCallback;
 import android.hardware.thermal.V2_0.IThermalChangedCallback;
+import android.hardware.thermal.V2_0.TemperatureThreshold;
 import android.hardware.thermal.V2_0.ThrottlingSeverity;
 import android.os.Binder;
 import android.os.CoolingDevice;
+import android.os.Handler;
 import android.os.HwBinder;
 import android.os.IThermalEventListener;
 import android.os.IThermalService;
@@ -36,6 +39,7 @@
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
 import android.os.ShellCommand;
+import android.os.SystemClock;
 import android.os.Temperature;
 import android.util.ArrayMap;
 import android.util.EventLog;
@@ -43,6 +47,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.DumpUtils;
 import com.android.server.EventLogTags;
 import com.android.server.FgThread;
@@ -54,6 +59,7 @@
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -100,6 +106,9 @@
     /** Hal ready. */
     private final AtomicBoolean mHalReady = new AtomicBoolean();
 
+    /** Watches temperatures to forecast when throttling will occur */
+    private final TemperatureWatcher mTemperatureWatcher = new TemperatureWatcher();
+
     /** Invalid throttling status */
     private static final int INVALID_THROTTLING = Integer.MIN_VALUE;
 
@@ -154,6 +163,7 @@
                 onTemperatureChanged(temperatures.get(i), false);
             }
             onTemperatureMapChangedLocked();
+            mTemperatureWatcher.updateSevereThresholds();
             mHalReady.set(true);
         }
     }
@@ -462,6 +472,15 @@
             }
         }
 
+        @Override
+        public float getThermalHeadroom(int forecastSeconds) {
+            if (!mHalReady.get()) {
+                return Float.NaN;
+            }
+
+            return mTemperatureWatcher.getForecast(forecastSeconds);
+        }
+
         private void dumpItemsLocked(PrintWriter pw, String prefix,
                 Collection<?> items) {
             for (Iterator iterator = items.iterator(); iterator.hasNext();) {
@@ -616,6 +635,10 @@
         protected abstract List<CoolingDevice> getCurrentCoolingDevices(boolean shouldFilter,
                 int type);
 
+        @NonNull
+        protected abstract List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+                int type);
+
         protected abstract boolean connectToHal();
 
         protected abstract void dump(PrintWriter pw, String prefix);
@@ -728,6 +751,12 @@
         }
 
         @Override
+        protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+                int type) {
+            return new ArrayList<>();
+        }
+
+        @Override
         protected boolean connectToHal() {
             synchronized (mHalLock) {
                 try {
@@ -857,6 +886,12 @@
         }
 
         @Override
+        protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+                int type) {
+            return new ArrayList<>();
+        }
+
+        @Override
         protected boolean connectToHal() {
             synchronized (mHalLock) {
                 try {
@@ -975,6 +1010,32 @@
         }
 
         @Override
+        protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+                int type) {
+            synchronized (mHalLock) {
+                List<TemperatureThreshold> ret = new ArrayList<>();
+                if (mThermalHal20 == null) {
+                    return ret;
+                }
+                try {
+                    mThermalHal20.getTemperatureThresholds(shouldFilter, type,
+                            (status, thresholds) -> {
+                                if (ThermalStatusCode.SUCCESS == status.code) {
+                                    ret.addAll(thresholds);
+                                } else {
+                                    Slog.e(TAG,
+                                            "Couldn't get temperature thresholds because of HAL "
+                                                    + "error: " + status.debugMessage);
+                                }
+                            });
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Couldn't getTemperatureThresholds, reconnecting...", e);
+                }
+                return ret;
+            }
+        }
+
+        @Override
         protected boolean connectToHal() {
             synchronized (mHalLock) {
                 try {
@@ -1001,4 +1062,190 @@
         }
     }
 
+    private class TemperatureWatcher {
+        private final Handler mHandler = BackgroundThread.getHandler();
+
+        /** Map of skin temperature sensor name to a corresponding list of samples */
+        @GuardedBy("mSamples")
+        private final ArrayMap<String, ArrayList<Sample>> mSamples = new ArrayMap<>();
+
+        /** Map of skin temperature sensor name to the corresponding SEVERE temperature threshold */
+        @GuardedBy("mSamples")
+        private ArrayMap<String, Float> mSevereThresholds = new ArrayMap<>();
+
+        @GuardedBy("mSamples")
+        private long mLastForecastCallTimeMillis = 0;
+
+        void updateSevereThresholds() {
+            synchronized (mSamples) {
+                List<TemperatureThreshold> thresholds =
+                        mHalWrapper.getTemperatureThresholds(true, Temperature.TYPE_SKIN);
+                for (int t = 0; t < thresholds.size(); ++t) {
+                    TemperatureThreshold threshold = thresholds.get(t);
+                    if (threshold.hotThrottlingThresholds.length <= ThrottlingSeverity.SEVERE) {
+                        continue;
+                    }
+                    float temperature =
+                            threshold.hotThrottlingThresholds[ThrottlingSeverity.SEVERE];
+                    if (!Float.isNaN(temperature)) {
+                        mSevereThresholds.put(threshold.name,
+                                threshold.hotThrottlingThresholds[ThrottlingSeverity.SEVERE]);
+                    }
+                }
+            }
+        }
+
+        private static final int INACTIVITY_THRESHOLD_MILLIS = 10000;
+        private static final int RING_BUFFER_SIZE = 30;
+
+        private void updateTemperature() {
+            synchronized (mSamples) {
+                if (SystemClock.elapsedRealtime() - mLastForecastCallTimeMillis
+                        < INACTIVITY_THRESHOLD_MILLIS) {
+                    // Trigger this again after a second as long as forecast has been called more
+                    // recently than the inactivity timeout
+                    mHandler.postDelayed(this::updateTemperature, 1000);
+                } else {
+                    // Otherwise, we've been idle for at least 10 seconds, so we should
+                    // shut down
+                    mSamples.clear();
+                    return;
+                }
+
+                long now = SystemClock.elapsedRealtime();
+                List<Temperature> temperatures = mHalWrapper.getCurrentTemperatures(true,
+                        Temperature.TYPE_SKIN);
+
+                for (int t = 0; t < temperatures.size(); ++t) {
+                    Temperature temperature = temperatures.get(t);
+
+                    // Filter out invalid temperatures. If this results in no values being stored at
+                    // all, the mSamples.empty() check in getForecast() will catch it.
+                    if (Float.isNaN(temperature.getValue())) {
+                        continue;
+                    }
+
+                    ArrayList<Sample> samples = mSamples.computeIfAbsent(temperature.getName(),
+                            k -> new ArrayList<>(RING_BUFFER_SIZE));
+                    if (samples.size() == RING_BUFFER_SIZE) {
+                        samples.remove(0);
+                    }
+                    samples.add(new Sample(now, temperature.getValue()));
+                }
+            }
+        }
+
+        /**
+         * Calculates the trend using a linear regression. As the samples are degrees Celsius with
+         * associated timestamps in milliseconds, the slope is in degrees Celsius per millisecond.
+         */
+        private float getSlopeOf(List<Sample> samples) {
+            long sumTimes = 0L;
+            float sumTemperatures = 0.0f;
+            for (int s = 0; s < samples.size(); ++s) {
+                Sample sample = samples.get(s);
+                sumTimes += sample.time;
+                sumTemperatures += sample.temperature;
+            }
+            long meanTime = sumTimes / samples.size();
+            float meanTemperature = sumTemperatures / samples.size();
+
+            long sampleVariance = 0L;
+            float sampleCovariance = 0.0f;
+            for (int s = 0; s < samples.size(); ++s) {
+                Sample sample = samples.get(s);
+                long timeDelta = sample.time - meanTime;
+                float temperatureDelta = sample.temperature - meanTemperature;
+                sampleVariance += timeDelta * timeDelta;
+                sampleCovariance += timeDelta * temperatureDelta;
+            }
+
+            return sampleCovariance / sampleVariance;
+        }
+
+        /**
+         * Used to determine the temperature corresponding to 0.0. Given that 1.0 is pinned at the
+         * temperature corresponding to the SEVERE threshold, we set 0.0 to be that temperature
+         * minus DEGREES_BETWEEN_ZERO_AND_ONE.
+         */
+        private static final float DEGREES_BETWEEN_ZERO_AND_ONE = 30.0f;
+
+        private float normalizeTemperature(float temperature, float severeThreshold) {
+            synchronized (mSamples) {
+                float zeroNormalized = severeThreshold - DEGREES_BETWEEN_ZERO_AND_ONE;
+                if (temperature <= zeroNormalized) {
+                    return 0.0f;
+                }
+                float delta = temperature - zeroNormalized;
+                return delta / DEGREES_BETWEEN_ZERO_AND_ONE;
+            }
+        }
+
+        private static final int MINIMUM_SAMPLE_COUNT = 3;
+
+        float getForecast(int forecastSeconds) {
+            synchronized (mSamples) {
+                mLastForecastCallTimeMillis = System.currentTimeMillis();
+                if (mSamples.isEmpty()) {
+                    updateTemperature();
+                }
+
+                // If somehow things take much longer than expected or there are no temperatures
+                // to sample, return early
+                if (mSamples.isEmpty()) {
+                    Slog.e(TAG, "No temperature samples found");
+                    return Float.NaN;
+                }
+
+                // If we don't have any thresholds, we can't normalize the temperatures,
+                // so return early
+                if (mSevereThresholds.isEmpty()) {
+                    Slog.e(TAG, "No temperature thresholds found");
+                    return Float.NaN;
+                }
+
+                float maxNormalized = Float.NaN;
+                for (Map.Entry<String, ArrayList<Sample>> entry : mSamples.entrySet()) {
+                    String name = entry.getKey();
+                    ArrayList<Sample> samples = entry.getValue();
+
+                    Float threshold = mSevereThresholds.get(name);
+                    if (threshold == null) {
+                        Slog.e(TAG, "No threshold found for " + name);
+                        continue;
+                    }
+
+                    float currentTemperature = samples.get(0).temperature;
+
+                    if (samples.size() < MINIMUM_SAMPLE_COUNT) {
+                        // Don't try to forecast, just use the latest one we have
+                        float normalized = normalizeTemperature(currentTemperature, threshold);
+                        if (Float.isNaN(maxNormalized) || normalized > maxNormalized) {
+                            maxNormalized = normalized;
+                        }
+                        continue;
+                    }
+
+                    float slope = getSlopeOf(samples);
+                    float normalized = normalizeTemperature(
+                            currentTemperature + slope * forecastSeconds * 1000, threshold);
+                    if (Float.isNaN(maxNormalized) || normalized > maxNormalized) {
+                        maxNormalized = normalized;
+                    }
+                }
+
+                return maxNormalized;
+            }
+        }
+
+        private class Sample {
+            public long time;
+            public float temperature;
+
+            Sample(long time, float temperature) {
+                this.time = time;
+                this.temperature = temperature;
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/twilight/TwilightService.java b/services/core/java/com/android/server/twilight/TwilightService.java
index 761fbf8..e72ba8d 100644
--- a/services/core/java/com/android/server/twilight/TwilightService.java
+++ b/services/core/java/com/android/server/twilight/TwilightService.java
@@ -50,7 +50,6 @@
         implements AlarmManager.OnAlarmListener, Handler.Callback, LocationListener {
 
     private static final String TAG = "TwilightService";
-    private static final String FEATURE_ID = "TwilightService";
     private static final boolean DEBUG = false;
 
     private static final int MSG_START_LISTENING = 1;
@@ -74,7 +73,7 @@
     protected TwilightState mLastTwilightState;
 
     public TwilightService(Context context) {
-        super(context.createFeatureContext(FEATURE_ID));
+        super(context);
         mHandler = new Handler(Looper.getMainLooper(), this);
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 840abb1..1b21117 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -16,7 +16,6 @@
 
 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.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
@@ -84,9 +83,6 @@
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
 
-import static com.android.server.wm.DisplayContentProto.FOCUSED_ROOT_TASK_ID;
-import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
-import static com.android.server.wm.DisplayContentProto.SINGLE_TASK_INSTANCE;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
@@ -105,12 +101,15 @@
 import static com.android.server.wm.DisplayContentProto.DOCKED_STACK_DIVIDER_CONTROLLER;
 import static com.android.server.wm.DisplayContentProto.DPI;
 import static com.android.server.wm.DisplayContentProto.FOCUSED_APP;
+import static com.android.server.wm.DisplayContentProto.FOCUSED_ROOT_TASK_ID;
 import static com.android.server.wm.DisplayContentProto.ID;
 import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
 import static com.android.server.wm.DisplayContentProto.OVERLAY_WINDOWS;
+import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
 import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
 import static com.android.server.wm.DisplayContentProto.ROTATION;
 import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
+import static com.android.server.wm.DisplayContentProto.SINGLE_TASK_INSTANCE;
 import static com.android.server.wm.DisplayContentProto.TASKS;
 import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
@@ -1553,6 +1552,20 @@
     }
 
     /**
+     * If the provided {@link ActivityRecord} can be displayed in an orientation different from the
+     * display's, it will be rotated to match its requested orientation.
+     *
+     * @see #rotationForActivityInDifferentOrientation(ActivityRecord).
+     * @see WindowToken#applyFixedRotationTransform(DisplayInfo, DisplayFrames, Configuration)
+     */
+    void rotateInDifferentOrientationIfNeeded(ActivityRecord activityRecord) {
+        int rotation = rotationForActivityInDifferentOrientation(activityRecord);
+        if (rotation != NO_ROTATION) {
+            startFixedRotationTransform(activityRecord, rotation);
+        }
+    }
+
+    /**
      * Update rotation of the display.
      *
      * @return {@code true} if the rotation has been changed.  In this case YOU MUST CALL
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 251d0f1..53dd326 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -34,6 +34,7 @@
 import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.app.ActivityManager.TaskSnapshot;
 import android.app.WindowConfiguration;
 import android.graphics.Point;
@@ -423,6 +424,11 @@
 
         mService.mWindowPlacerLocked.performSurfacePlacement();
 
+        // If the target activity has a fixed orientation which is different from the current top
+        // activity, it will be rotated before being shown so we avoid a screen rotation
+        // animation when showing the Recents view.
+        mDisplayContent.rotateInDifferentOrientationIfNeeded(mTargetActivityRecord);
+
         // Notify that the animation has started
         if (mStatusBar != null) {
             mStatusBar.onRecentsAnimationStateChanged(true /* running */);
@@ -695,6 +701,9 @@
                 mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(
                         mTargetActivityRecord.token);
             }
+            if (mTargetActivityRecord.hasFixedRotationTransform()) {
+                mTargetActivityRecord.clearFixedRotationTransform();
+            }
         }
 
         // Notify that the animation has ended
@@ -828,6 +837,19 @@
         return task != null && isAnimatingTask(task) && !isTargetApp(windowState.mActivityRecord);
     }
 
+    /**
+     * If the animation target ActivityRecord has a fixed rotation ({@link
+     * WindowToken#hasFixedRotationTransform()}, the provided wallpaper will be rotated accordingly.
+     *
+     * This avoids any screen rotation animation when animating to the Recents view.
+     */
+    void applyFixedRotationTransformIfNeeded(@NonNull WindowToken wallpaper) {
+        if (mTargetActivityRecord == null) {
+            return;
+        }
+        wallpaper.applyFixedRotationTransform(mTargetActivityRecord);
+    }
+
     @VisibleForTesting
     class TaskAnimationAdapter implements AnimationAdapter {
 
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index d23bf97..1e22141 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -122,10 +122,37 @@
             mDisplayContent.setLayoutNeeded();
         }
 
-        final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
+        final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
+
+        if (visible) {
+            final WindowState wallpaperTarget = wallpaperController.getWallpaperTarget();
+            final RecentsAnimationController recentsAnimationController =
+                    mWmService.getRecentsAnimationController();
+            if (wallpaperTarget != null
+                    && recentsAnimationController != null
+                    && recentsAnimationController.isAnimatingTask(wallpaperTarget.getTask())) {
+                // If the Recents animation is running, and the wallpaper target is the animating
+                // task we want the wallpaper to be rotated in the same orientation as the
+                // RecentsAnimation's target (e.g the launcher)
+                recentsAnimationController.applyFixedRotationTransformIfNeeded(this);
+            } else if (wallpaperTarget != null
+                    && wallpaperTarget.mToken.hasFixedRotationTransform()) {
+                // If the wallpaper target has a fixed rotation, we want the wallpaper to follow its
+                // rotation
+                applyFixedRotationTransform(wallpaperTarget.mToken);
+            } else if (hasFixedRotationTransform()) {
+                clearFixedRotationTransform();
+            }
+        }
+
+        DisplayInfo displayInfo = getFixedRotationTransformDisplayInfo();
+        if (displayInfo == null) {
+            displayInfo = mDisplayContent.getDisplayInfo();
+        }
+
         final int dw = displayInfo.logicalWidth;
         final int dh = displayInfo.logicalHeight;
-        final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
+
         for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
             final WindowState wallpaper = mChildren.get(wallpaperNdx);
 
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 1180566..48c7812 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -377,6 +377,19 @@
         onConfigurationChanged(getParent().getConfiguration());
     }
 
+    /**
+     * Copies the {@link FixedRotationTransformState} (if any) from the other WindowToken to this
+     * one.
+     */
+    void applyFixedRotationTransform(WindowToken other) {
+        final FixedRotationTransformState fixedRotationState = other.mFixedRotationTransformState;
+        if (fixedRotationState != null) {
+            applyFixedRotationTransform(fixedRotationState.mDisplayInfo,
+                    fixedRotationState.mDisplayFrames,
+                    fixedRotationState.mRotatedOverrideConfiguration);
+        }
+    }
+
     /** Clears the transformation and continue updating the orientation change of display. */
     void clearFixedRotationTransform() {
         if (mFixedRotationTransformState == null) {
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 663bf4f..2499614 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -88,7 +88,7 @@
         @Override
         public void onCreatePredictionSession(AppPredictionContext context,
                 AppPredictionSessionId sessionId) {
-            mSessions.put(sessionId, new SessionInfo(context, mDataManager));
+            mSessions.put(sessionId, new SessionInfo(context, mDataManager, sessionId.getUserId()));
         }
 
         @Override
diff --git a/services/people/java/com/android/server/people/SessionInfo.java b/services/people/java/com/android/server/people/SessionInfo.java
index eaa0781..28612f1 100644
--- a/services/people/java/com/android/server/people/SessionInfo.java
+++ b/services/people/java/com/android/server/people/SessionInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.server.people;
 
+import android.annotation.UserIdInt;
 import android.app.prediction.AppPredictionContext;
 import android.app.prediction.AppTarget;
 import android.app.prediction.IPredictionCallback;
@@ -38,9 +39,10 @@
     private final RemoteCallbackList<IPredictionCallback> mCallbacks =
             new RemoteCallbackList<>();
 
-    SessionInfo(AppPredictionContext predictionContext, DataManager dataManager) {
+    SessionInfo(AppPredictionContext predictionContext, DataManager dataManager,
+            @UserIdInt int callingUserId) {
         mAppTargetPredictor = AppTargetPredictor.create(predictionContext,
-                this::updatePredictions, dataManager);
+                this::updatePredictions, dataManager, callingUserId);
     }
 
     void addCallback(IPredictionCallback callback) {
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index dd9cbd0..6b97c98 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -34,13 +34,11 @@
 import android.content.pm.LauncherApps.ShortcutQuery;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ShortcutInfo;
-import android.content.pm.ShortcutManager;
 import android.content.pm.ShortcutManager.ShareShortcutInfo;
 import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.net.Uri;
-import android.os.Binder;
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.Process;
@@ -83,7 +81,6 @@
  */
 public class DataManager {
 
-    private static final String PLATFORM_PACKAGE_NAME = "android";
     private static final int MY_UID = Process.myUid();
     private static final int MY_PID = Process.myPid();
     private static final long QUERY_EVENTS_MAX_AGE_MS = DateUtils.DAY_IN_MILLIS;
@@ -106,7 +103,6 @@
 
     private ShortcutServiceInternal mShortcutServiceInternal;
     private PackageManagerInternal mPackageManagerInternal;
-    private ShortcutManager mShortcutManager;
     private UserManager mUserManager;
 
     public DataManager(Context context) {
@@ -125,7 +121,6 @@
     public void initialize() {
         mShortcutServiceInternal = LocalServices.getService(ShortcutServiceInternal.class);
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
-        mShortcutManager = mContext.getSystemService(ShortcutManager.class);
         mUserManager = mContext.getSystemService(UserManager.class);
 
         mShortcutServiceInternal.addListener(new ShortcutServiceListener());
@@ -171,8 +166,7 @@
         mNotificationListeners.put(userId, notificationListener);
         try {
             notificationListener.registerAsSystemService(mContext,
-                    new ComponentName(PLATFORM_PACKAGE_NAME, getClass().getCanonicalName()),
-                    userId);
+                    new ComponentName(mContext, getClass()), userId);
         } catch (RemoteException e) {
             // Should never occur for local calls.
         }
@@ -242,8 +236,8 @@
      * Iterates through all the {@link PackageData}s owned by the unlocked users who are in the
      * same profile group as the calling user.
      */
-    public void forAllPackages(Consumer<PackageData> consumer) {
-        List<UserInfo> users = mUserManager.getEnabledProfiles(mInjector.getCallingUserId());
+    void forPackagesInProfile(@UserIdInt int callingUserId, Consumer<PackageData> consumer) {
+        List<UserInfo> users = mUserManager.getEnabledProfiles(callingUserId);
         for (UserInfo userInfo : users) {
             UserData userData = getUnlockedUserData(userInfo.id);
             if (userData != null) {
@@ -275,8 +269,10 @@
      * Gets the {@link ShareShortcutInfo}s from all packages owned by the calling user that match
      * the specified {@link IntentFilter}.
      */
-    public List<ShareShortcutInfo> getShareShortcuts(@NonNull IntentFilter intentFilter) {
-        return mShortcutManager.getShareTargets(intentFilter);
+    public List<ShareShortcutInfo> getShareShortcuts(@NonNull IntentFilter intentFilter,
+            @UserIdInt int callingUserId) {
+        return mShortcutServiceInternal.getShareTargets(
+                mContext.getPackageName(), intentFilter, callingUserId);
     }
 
     /** Reports the {@link AppTargetEvent} from App Prediction Manager. */
@@ -361,7 +357,7 @@
         @ShortcutQuery.QueryFlags int queryFlags = ShortcutQuery.FLAG_MATCH_DYNAMIC
                 | ShortcutQuery.FLAG_MATCH_PINNED | ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER;
         return mShortcutServiceInternal.getShortcuts(
-                mInjector.getCallingUserId(), /*callingPackage=*/ PLATFORM_PACKAGE_NAME,
+                UserHandle.USER_SYSTEM, mContext.getPackageName(),
                 /*changedSince=*/ 0, packageName, shortcutIds, /*locusIds=*/ null,
                 /*componentName=*/ null, queryFlags, userId, MY_PID, MY_UID);
     }
@@ -775,7 +771,7 @@
 
         @Override
         public void onReceive(Context context, Intent intent) {
-            forAllPackages(PackageData::saveToDisk);
+            forAllUnlockedUsers(userData -> userData.forAllPackages(PackageData::saveToDisk));
         }
     }
 
@@ -809,9 +805,5 @@
                 Function<String, PackageData> packageDataGetter) {
             return new UsageStatsQueryHelper(userId, packageDataGetter);
         }
-
-        int getCallingUserId() {
-            return Binder.getCallingUserHandle().getIdentifier();
-        }
     }
 }
diff --git a/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
index 44f3e35..19cf8af 100644
--- a/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
@@ -18,6 +18,7 @@
 
 import android.annotation.MainThread;
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.annotation.WorkerThread;
 import android.app.prediction.AppPredictionContext;
 import android.app.prediction.AppTarget;
@@ -42,25 +43,28 @@
     /** Creates a {@link AppTargetPredictor} instance based on the prediction context. */
     public static AppTargetPredictor create(@NonNull AppPredictionContext predictionContext,
             @NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
-            @NonNull DataManager dataManager) {
+            @NonNull DataManager dataManager, @UserIdInt int callingUserId) {
         if (UI_SURFACE_SHARE.equals(predictionContext.getUiSurface())) {
             return new ShareTargetPredictor(
-                    predictionContext, updatePredictionsMethod, dataManager);
+                    predictionContext, updatePredictionsMethod, dataManager, callingUserId);
         }
-        return new AppTargetPredictor(predictionContext, updatePredictionsMethod, dataManager);
+        return new AppTargetPredictor(
+                predictionContext, updatePredictionsMethod, dataManager, callingUserId);
     }
 
     private final AppPredictionContext mPredictionContext;
     private final Consumer<List<AppTarget>> mUpdatePredictionsMethod;
     private final DataManager mDataManager;
+    final int mCallingUserId;
     private final ExecutorService mCallbackExecutor;
 
     AppTargetPredictor(@NonNull AppPredictionContext predictionContext,
             @NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
-            @NonNull DataManager dataManager) {
+            @NonNull DataManager dataManager, @UserIdInt int callingUserId) {
         mPredictionContext = predictionContext;
         mUpdatePredictionsMethod = updatePredictionsMethod;
         mDataManager = dataManager;
+        mCallingUserId = callingUserId;
         mCallbackExecutor = Executors.newSingleThreadExecutor();
     }
 
diff --git a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
index 280ced3..90d8216 100644
--- a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
@@ -19,6 +19,7 @@
 import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.annotation.WorkerThread;
 import android.app.prediction.AppPredictionContext;
 import android.app.prediction.AppTarget;
@@ -45,8 +46,8 @@
 
     ShareTargetPredictor(@NonNull AppPredictionContext predictionContext,
             @NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
-            @NonNull DataManager dataManager) {
-        super(predictionContext, updatePredictionsMethod, dataManager);
+            @NonNull DataManager dataManager, @UserIdInt int callingUserId) {
+        super(predictionContext, updatePredictionsMethod, dataManager, callingUserId);
         mIntentFilter = predictionContext.getExtras().getParcelable(
                 ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY);
     }
@@ -84,7 +85,7 @@
     List<ShareTarget> getShareTargets() {
         List<ShareTarget> shareTargets = new ArrayList<>();
         List<ShareShortcutInfo> shareShortcuts =
-                getDataManager().getShareShortcuts(mIntentFilter);
+                getDataManager().getShareShortcuts(mIntentFilter, mCallingUserId);
 
         for (ShareShortcutInfo shareShortcut : shareShortcuts) {
             ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
diff --git a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
index 2a78b6f..2c84f26 100644
--- a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
@@ -26,6 +26,7 @@
 import android.os.BatteryManager;
 import android.os.BatteryManagerInternal;
 import android.os.IPowerManager;
+import android.os.IThermalService;
 import android.os.OsProtoEnums;
 import android.os.PowerManager;
 import android.os.RemoteException;
@@ -52,13 +53,14 @@
 public class CachedDeviceStateServiceTest {
     @Mock private BatteryManagerInternal mBatteryManager;
     @Mock private IPowerManager mPowerManager;
+    @Mock private IThermalService mThermalService;
     private BroadcastInterceptingContext mContext;
 
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
         Context context = InstrumentationRegistry.getContext();
-        PowerManager powerManager = new PowerManager(context, mPowerManager, null);
+        PowerManager powerManager = new PowerManager(context, mPowerManager, mThermalService, null);
         mContext = new BroadcastInterceptingContext(context) {
             @Override
             public Object getSystemService(String name) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 69ca643..ae8d554 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -74,6 +74,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IPowerManager;
+import android.os.IThermalService;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteCallback;
@@ -147,6 +148,7 @@
 
     @Mock private Context mMockContext;
     @Mock private IPowerManager mMockIPowerManager;
+    @Mock private IThermalService mMockIThermalService;
     @Mock private PackageManager mMockPackageManager;
     @Spy  private AccessibilityServiceInfo mSpyServiceInfo = new AccessibilityServiceInfo();
     @Mock private AccessibilitySecurityPolicy mMockSecurityPolicy;
@@ -174,7 +176,7 @@
                 .thenReturn(mMockMagnificationController);
 
         PowerManager powerManager =
-                new PowerManager(mMockContext, mMockIPowerManager, mHandler);
+                new PowerManager(mMockContext, mMockIPowerManager, mMockIThermalService, mHandler);
         when(mMockContext.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
         when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
         when(mMockPackageManager.hasSystemFeature(FEATURE_FINGERPRINT)).thenReturn(true);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
index 4123556..85b8fcb 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
@@ -34,6 +34,7 @@
 import android.content.Context;
 import android.os.Handler;
 import android.os.IPowerManager;
+import android.os.IThermalService;
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
@@ -73,6 +74,7 @@
     private KeyEventFilter mKeyEventFilter1;
     private KeyEventFilter mKeyEventFilter2;
     private IPowerManager mMockPowerManagerService;
+    private IThermalService mMockThermalService;
     private MessageCapturingHandler mMessageCapturingHandler;
     private ArgumentCaptor<Integer> mFilter1SequenceCaptor = ArgumentCaptor.forClass(Integer.class);
     private ArgumentCaptor<Integer> mFilter2SequenceCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -82,10 +84,12 @@
         Looper looper = InstrumentationRegistry.getContext().getMainLooper();
         mInputEventsHandler = new MessageCapturingHandler(looper, null);
         mMockPowerManagerService = mock(IPowerManager.class);
+        mMockThermalService = mock(IThermalService.class);
         // TODO: It would be better to mock PowerManager rather than its binder, but the class is
         // final.
         PowerManager powerManager =
-                new PowerManager(mock(Context.class), mMockPowerManagerService, new Handler(looper));
+                new PowerManager(mock(Context.class), mMockPowerManagerService, mMockThermalService,
+                        new Handler(looper));
         mMessageCapturingHandler = new MessageCapturingHandler(looper, null);
         mKeyEventDispatcher = new KeyEventDispatcher(mInputEventsHandler, SEND_FRAMEWORK_KEY_EVENT,
                 mLock, powerManager, mMessageCapturingHandler);
diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
index e90cb46..ac0cac1 100644
--- a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
@@ -37,6 +37,7 @@
 import android.content.Context;
 import android.os.IBinder;
 import android.os.IPowerManager;
+import android.os.IThermalService;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.provider.DeviceConfig;
@@ -74,6 +75,8 @@
     @Mock
     private IPowerManager mMockIPowerManager;
     @Mock
+    private IThermalService mMockIThermalService;
+    @Mock
     Context mContext;
 
     @Before
@@ -84,7 +87,7 @@
         // setup power manager mock
         PowerManager mPowerManager;
         doReturn(true).when(mMockIPowerManager).isInteractive();
-        mPowerManager = new PowerManager(mContext, mMockIPowerManager, null);
+        mPowerManager = new PowerManager(mContext, mMockIPowerManager, mMockIThermalService, null);
 
         Object mLock = new Object();
         // setup a spy on attention manager
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 9e98427..fa19814 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -34,6 +34,7 @@
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiPortInfo;
 import android.os.IPowerManager;
+import android.os.IThermalService;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.RemoteException;
@@ -123,6 +124,7 @@
     private HdmiPortInfo[] mHdmiPortInfo;
 
     @Mock private IPowerManager mIPowerManagerMock;
+    @Mock private IThermalService mIThermalServiceMock;
 
     @Before
     public void setUp() throws Exception {
@@ -130,7 +132,8 @@
 
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
 
-        PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock, null);
+        PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, null);
         when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
         when(mIPowerManagerMock.isInteractive()).thenReturn(true);
 
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 3ecd319..5e104a5 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -54,7 +54,6 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ShortcutInfo;
-import android.content.pm.ShortcutManager;
 import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.UserInfo;
 import android.content.pm.parsing.AndroidPackage;
@@ -87,6 +86,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
@@ -110,7 +110,6 @@
     @Mock private ShortcutServiceInternal mShortcutServiceInternal;
     @Mock private UsageStatsManagerInternal mUsageStatsManagerInternal;
     @Mock private PackageManagerInternal mPackageManagerInternal;
-    @Mock private ShortcutManager mShortcutManager;
     @Mock private UserManager mUserManager;
     @Mock private TelephonyManager mTelephonyManager;
     @Mock private TelecomManager mTelecomManager;
@@ -123,7 +122,6 @@
 
     private NotificationChannel mNotificationChannel;
     private DataManager mDataManager;
-    private int mCallingUserId;
     private CancellationSignal mCancellationSignal;
     private TestInjector mInjector;
 
@@ -145,14 +143,11 @@
         }).when(mPackageManagerInternal).forEachInstalledPackage(any(Consumer.class), anyInt());
 
         when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+        when(mContext.getPackageName()).thenReturn("android");
 
         Context originalContext = getInstrumentation().getTargetContext();
         when(mContext.getApplicationInfo()).thenReturn(originalContext.getApplicationInfo());
 
-        when(mContext.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(mShortcutManager);
-        when(mContext.getSystemServiceName(ShortcutManager.class)).thenReturn(
-                Context.SHORTCUT_SERVICE);
-
         when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
         when(mContext.getSystemServiceName(UserManager.class)).thenReturn(
                 Context.USER_SERVICE);
@@ -191,8 +186,6 @@
                 NOTIFICATION_CHANNEL_ID, "test channel", NotificationManager.IMPORTANCE_DEFAULT);
         mNotificationChannel.setConversationId("test", TEST_SHORTCUT_ID);
 
-        mCallingUserId = USER_ID_PRIMARY;
-
         mCancellationSignal = new CancellationSignal();
 
         mInjector = new TestInjector();
@@ -222,9 +215,7 @@
         mDataManager.onShortcutAddedOrUpdated(
                 buildShortcutInfo("pkg_3", USER_ID_SECONDARY, "sc_3", buildPerson()));
 
-        List<ConversationInfo> conversations = new ArrayList<>();
-        mDataManager.forAllPackages(
-                packageData -> packageData.forAllConversations(conversations::add));
+        List<ConversationInfo> conversations = getConversationsInPrimary();
 
         // USER_ID_SECONDARY is not in the same profile group as USER_ID_PRIMARY.
         assertEquals(2, conversations.size());
@@ -250,18 +241,14 @@
         mDataManager.onShortcutAddedOrUpdated(
                 buildShortcutInfo("pkg_2", USER_ID_PRIMARY_MANAGED, "sc_2", buildPerson()));
 
-        List<ConversationInfo> conversations = new ArrayList<>();
-        mDataManager.forAllPackages(
-                packageData -> packageData.forAllConversations(conversations::add));
+        List<ConversationInfo> conversations = getConversationsInPrimary();
 
         // USER_ID_PRIMARY_MANAGED is not locked, so only USER_ID_PRIMARY's conversation is stored.
         assertEquals(1, conversations.size());
         assertEquals("sc_1", conversations.get(0).getShortcutId());
 
         mDataManager.onUserStopped(USER_ID_PRIMARY);
-        conversations.clear();
-        mDataManager.forAllPackages(
-                packageData -> packageData.forAllConversations(conversations::add));
+        conversations = getConversationsInPrimary();
         assertTrue(conversations.isEmpty());
     }
 
@@ -289,12 +276,8 @@
         IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
         mDataManager.reportAppTargetEvent(appTargetEvent, intentFilter);
 
-        List<Range<Long>> activeShareTimeSlots = new ArrayList<>();
-        mDataManager.forAllPackages(packageData ->
-                activeShareTimeSlots.addAll(
-                        packageData.getEventHistory(TEST_SHORTCUT_ID)
-                                .getEventIndex(Event.TYPE_SHARE_IMAGE)
-                                .getActiveTimeSlots()));
+        List<Range<Long>> activeShareTimeSlots = getActiveSlotsForTestShortcut(
+                Event.SHARE_EVENT_TYPES);
         assertEquals(1, activeShareTimeSlots.size());
     }
 
@@ -315,9 +298,7 @@
                 USER_ID_PRIMARY);
         contentObserver.onChange(false, ContactsContract.Contacts.CONTENT_URI, USER_ID_PRIMARY);
 
-        List<ConversationInfo> conversations = new ArrayList<>();
-        mDataManager.forAllPackages(
-                packageData -> packageData.forAllConversations(conversations::add));
+        List<ConversationInfo> conversations = getConversationsInPrimary();
         assertEquals(1, conversations.size());
 
         assertEquals(TEST_SHORTCUT_ID, conversations.get(0).getShortcutId());
@@ -338,12 +319,8 @@
 
         listenerService.onNotificationPosted(mStatusBarNotification);
 
-        List<Range<Long>> activeNotificationOpenTimeSlots = new ArrayList<>();
-        mDataManager.forAllPackages(packageData ->
-                activeNotificationOpenTimeSlots.addAll(
-                        packageData.getEventHistory(TEST_SHORTCUT_ID)
-                                .getEventIndex(Event.TYPE_NOTIFICATION_POSTED)
-                                .getActiveTimeSlots()));
+        List<Range<Long>> activeNotificationOpenTimeSlots = getActiveSlotsForTestShortcut(
+                Event.NOTIFICATION_EVENT_TYPES);
         assertEquals(1, activeNotificationOpenTimeSlots.size());
     }
 
@@ -361,12 +338,8 @@
         listenerService.onNotificationRemoved(mStatusBarNotification, null,
                 NotificationListenerService.REASON_CLICK);
 
-        List<Range<Long>> activeNotificationOpenTimeSlots = new ArrayList<>();
-        mDataManager.forAllPackages(packageData ->
-                activeNotificationOpenTimeSlots.addAll(
-                        packageData.getEventHistory(TEST_SHORTCUT_ID)
-                                .getEventIndex(Event.TYPE_NOTIFICATION_OPENED)
-                                .getActiveTimeSlots()));
+        List<Range<Long>> activeNotificationOpenTimeSlots = getActiveSlotsForTestShortcut(
+                Event.NOTIFICATION_EVENT_TYPES);
         assertEquals(1, activeNotificationOpenTimeSlots.size());
     }
 
@@ -469,12 +442,7 @@
         mInjector.mCallLogQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
                 new Event(currentTimestamp - MILLIS_PER_MINUTE * 5L, Event.TYPE_CALL_MISSED));
 
-        List<Range<Long>> activeTimeSlots = new ArrayList<>();
-        mDataManager.forAllPackages(packageData ->
-                activeTimeSlots.addAll(
-                        packageData.getEventHistory(TEST_SHORTCUT_ID)
-                                .getEventIndex(Event.CALL_EVENT_TYPES)
-                                .getActiveTimeSlots()));
+        List<Range<Long>> activeTimeSlots = getActiveSlotsForTestShortcut(Event.CALL_EVENT_TYPES);
         assertEquals(3, activeTimeSlots.size());
     }
 
@@ -498,12 +466,7 @@
         mInjector.mMmsQueryHelper.mEventConsumer.accept(PHONE_NUMBER, outgoingSmsEvent);
         mInjector.mSmsQueryHelper.mEventConsumer.accept(PHONE_NUMBER, incomingSmsEvent);
 
-        List<Range<Long>> activeTimeSlots = new ArrayList<>();
-        mDataManager.forAllPackages(packageData ->
-                activeTimeSlots.addAll(
-                        packageData.getEventHistory(TEST_SHORTCUT_ID)
-                                .getEventIndex(Event.SMS_EVENT_TYPES)
-                                .getActiveTimeSlots()));
+        List<Range<Long>> activeTimeSlots = getActiveSlotsForTestShortcut(Event.SMS_EVENT_TYPES);
         assertEquals(2, activeTimeSlots.size());
     }
 
@@ -551,22 +514,12 @@
         mInjector.mCallLogQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
                 new Event(currentTimestamp - MILLIS_PER_MINUTE, Event.TYPE_CALL_OUTGOING));
 
-        List<Range<Long>> activeTimeSlots = new ArrayList<>();
-        mDataManager.forAllPackages(packageData ->
-                activeTimeSlots.addAll(
-                        packageData.getEventHistory(TEST_SHORTCUT_ID)
-                                .getEventIndex(Event.CALL_EVENT_TYPES)
-                                .getActiveTimeSlots()));
+        List<Range<Long>> activeTimeSlots = getActiveSlotsForTestShortcut(Event.CALL_EVENT_TYPES);
         assertEquals(1, activeTimeSlots.size());
 
         mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultDialer(null);
         mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
-        activeTimeSlots.clear();
-        mDataManager.forAllPackages(packageData ->
-                activeTimeSlots.addAll(
-                        packageData.getEventHistory(TEST_SHORTCUT_ID)
-                                .getEventIndex(Event.CALL_EVENT_TYPES)
-                                .getActiveTimeSlots()));
+        activeTimeSlots = getActiveSlotsForTestShortcut(Event.CALL_EVENT_TYPES);
         assertTrue(activeTimeSlots.isEmpty());
     }
 
@@ -583,22 +536,12 @@
         mInjector.mMmsQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
                 new Event(currentTimestamp - MILLIS_PER_MINUTE, Event.TYPE_SMS_OUTGOING));
 
-        List<Range<Long>> activeTimeSlots = new ArrayList<>();
-        mDataManager.forAllPackages(packageData ->
-                activeTimeSlots.addAll(
-                        packageData.getEventHistory(TEST_SHORTCUT_ID)
-                                .getEventIndex(Event.SMS_EVENT_TYPES)
-                                .getActiveTimeSlots()));
+        List<Range<Long>> activeTimeSlots = getActiveSlotsForTestShortcut(Event.SMS_EVENT_TYPES);
         assertEquals(1, activeTimeSlots.size());
 
         mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(null);
         mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
-        activeTimeSlots.clear();
-        mDataManager.forAllPackages(packageData ->
-                activeTimeSlots.addAll(
-                        packageData.getEventHistory(TEST_SHORTCUT_ID)
-                                .getEventIndex(Event.SMS_EVENT_TYPES)
-                                .getActiveTimeSlots()));
+        activeTimeSlots = getActiveSlotsForTestShortcut(Event.SMS_EVENT_TYPES);
         assertTrue(activeTimeSlots.isEmpty());
     }
 
@@ -607,6 +550,24 @@
         LocalServices.addService(clazz, mock);
     }
 
+    private List<ConversationInfo> getConversationsInPrimary() {
+        List<ConversationInfo> conversations = new ArrayList<>();
+        mDataManager.forPackagesInProfile(USER_ID_PRIMARY,
+                packageData -> packageData.forAllConversations(conversations::add));
+        return conversations;
+    }
+
+    private List<Range<Long>> getActiveSlotsForTestShortcut(
+            Set<Integer> eventTypes) {
+        List<Range<Long>> activeSlots = new ArrayList<>();
+        mDataManager.forPackagesInProfile(USER_ID_PRIMARY, packageData ->
+                activeSlots.addAll(
+                        packageData.getEventHistory(TEST_SHORTCUT_ID)
+                                .getEventIndex(eventTypes)
+                                .getActiveTimeSlots()));
+        return activeSlots;
+    }
+
     private ShortcutInfo buildShortcutInfo(String packageName, int userId, String id,
             @Nullable Person person) {
         Context mockContext = mock(Context.class);
@@ -778,10 +739,5 @@
             mSmsQueryHelper = new TestSmsQueryHelper(context, eventConsumer);
             return mSmsQueryHelper;
         }
-
-        @Override
-        int getCallingUserId() {
-            return mCallingUserId;
-        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
index 808906e..f498a94 100644
--- a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -73,7 +74,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        when(mDataManager.getShareShortcuts(any())).thenReturn(mShareShortcuts);
+        when(mDataManager.getShareShortcuts(any(), anyInt())).thenReturn(mShareShortcuts);
         when(mDataManager.getPackage(PACKAGE_1, USER_ID)).thenReturn(mPackageData1);
         when(mDataManager.getPackage(PACKAGE_2, USER_ID)).thenReturn(mPackageData2);
 
@@ -82,7 +83,8 @@
                 .setPredictedTargetCount(NUM_PREDICTED_TARGETS)
                 .setExtras(new Bundle())
                 .build();
-        mPredictor = new ShareTargetPredictor(predictionContext, targets -> { }, mDataManager);
+        mPredictor = new ShareTargetPredictor(
+                predictionContext, targets -> { }, mDataManager, USER_ID);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
index ccf7ca9..624cb83 100644
--- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
@@ -28,10 +28,12 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.hardware.thermal.V2_0.TemperatureThreshold;
 import android.os.CoolingDevice;
 import android.os.IBinder;
 import android.os.IPowerManager;
 import android.os.IThermalEventListener;
+import android.os.IThermalService;
 import android.os.IThermalStatusListener;
 import android.os.PowerManager;
 import android.os.RemoteException;
@@ -72,6 +74,8 @@
     @Mock
     private IPowerManager mIPowerManagerMock;
     @Mock
+    private IThermalService mIThermalServiceMock;
+    @Mock
     private IThermalEventListener mEventListener1;
     @Mock
     private IThermalEventListener mEventListener2;
@@ -133,6 +137,12 @@
         }
 
         @Override
+        protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+                int type) {
+            return new ArrayList<>();
+        }
+
+        @Override
         protected boolean connectToHal() {
             return true;
         }
@@ -153,7 +163,7 @@
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
         mFakeHal = new ThermalHalFake();
-        mPowerManager = new PowerManager(mContext, mIPowerManagerMock, null);
+        mPowerManager = new PowerManager(mContext, mIPowerManagerMock, mIThermalServiceMock, null);
         when(mContext.getSystemServiceName(PowerManager.class)).thenReturn(Context.POWER_SERVICE);
         when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
         resetListenerMock();
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
index d5cdbeb..035a2f1 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
@@ -36,6 +36,7 @@
 import android.os.Handler;
 import android.os.IPowerManager;
 import android.os.IRecoverySystemProgressListener;
+import android.os.IThermalService;
 import android.os.Looper;
 import android.os.PowerManager;
 
@@ -62,6 +63,7 @@
     private RecoverySystemService.UncryptSocket mUncryptSocket;
     private Context mContext;
     private IPowerManager mIPowerManager;
+    private IThermalService mIThermalService;
     private FileWriter mUncryptUpdateFileWriter;
     private LockSettingsInternal mLockSettingsInternal;
 
@@ -77,8 +79,9 @@
 
         Looper looper = InstrumentationRegistry.getContext().getMainLooper();
         mIPowerManager = mock(IPowerManager.class);
+        mIThermalService = mock(IThermalService.class);
         PowerManager powerManager = new PowerManager(mock(Context.class), mIPowerManager,
-                new Handler(looper));
+                mIThermalService, new Handler(looper));
 
         mRecoverySystemService = new RecoverySystemServiceTestable(mContext, mSystemProperties,
                 powerManager, mUncryptUpdateFileWriter, mUncryptSocket, mLockSettingsInternal);
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 604fcd3..ccce043 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -359,8 +359,12 @@
 
     @Before
     public void setUp() throws Exception {
+        // Shell permisssions will override permissions of our app, so add all necessary permissions
+        // fo this test here:
         InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
-                "android.permission.WRITE_DEVICE_CONFIG", "android.permission.READ_DEVICE_CONFIG");
+                "android.permission.WRITE_DEVICE_CONFIG",
+                "android.permission.READ_DEVICE_CONFIG",
+                "android.permission.READ_CONTACTS");
 
         MockitoAnnotations.initMocks(this);
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 0adf15c..7f9732b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -108,6 +108,7 @@
     private static final int UID_N_MR1 = 0;
     private static final UserHandle USER = UserHandle.of(0);
     private static final int UID_O = 1111;
+    private static final int UID_P = 2222;
     private static final String SYSTEM_PKG = "android";
     private static final int SYSTEM_UID = 1000;
     private static final UserHandle USER2 = UserHandle.of(10);
@@ -141,9 +142,11 @@
         upgrade.targetSdkVersion = Build.VERSION_CODES.O;
         when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(legacy);
         when(mPm.getApplicationInfoAsUser(eq(PKG_O), anyInt(), anyInt())).thenReturn(upgrade);
+        when(mPm.getApplicationInfoAsUser(eq(PKG_P), anyInt(), anyInt())).thenReturn(upgrade);
         when(mPm.getApplicationInfoAsUser(eq(SYSTEM_PKG), anyInt(), anyInt())).thenReturn(upgrade);
         when(mPm.getPackageUidAsUser(eq(PKG_N_MR1), anyInt())).thenReturn(UID_N_MR1);
         when(mPm.getPackageUidAsUser(eq(PKG_O), anyInt())).thenReturn(UID_O);
+        when(mPm.getPackageUidAsUser(eq(PKG_P), anyInt())).thenReturn(UID_P);
         when(mPm.getPackageUidAsUser(eq(SYSTEM_PKG), anyInt())).thenReturn(SYSTEM_UID);
         PackageInfo info = mock(PackageInfo.class);
         info.signatures = new Signature[] {mock(Signature.class)};
@@ -2945,6 +2948,92 @@
     }
 
     @Test
+    public void testGetConversations_all() {
+        String convoId = "convo";
+        NotificationChannel messages =
+                new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(PKG_O, UID_O, messages, true, false);
+        NotificationChannel calls =
+                new NotificationChannel("calls", "Calls", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(PKG_O, UID_O, calls, true, false);
+        NotificationChannel p =
+                new NotificationChannel("p calls", "Calls", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(PKG_P, UID_P, p, true, false);
+
+        NotificationChannel channel =
+                new NotificationChannel("A person msgs", "messages from A", IMPORTANCE_DEFAULT);
+        channel.setConversationId(messages.getId(), convoId);
+        mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+
+        NotificationChannel diffConvo =
+                new NotificationChannel("B person msgs", "messages from B", IMPORTANCE_DEFAULT);
+        diffConvo.setConversationId(p.getId(), "different convo");
+        mHelper.createNotificationChannel(PKG_P, UID_P, diffConvo, true, false);
+
+        NotificationChannel channel2 =
+                new NotificationChannel("A person calls", "calls from A", IMPORTANCE_DEFAULT);
+        channel2.setConversationId(calls.getId(), convoId);
+        channel2.setImportantConversation(true);
+        mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false);
+
+        List<ConversationChannelWrapper> convos = mHelper.getConversations(false);
+
+        assertEquals(3, convos.size());
+        assertTrue(conversationWrapperContainsChannel(convos, channel));
+        assertTrue(conversationWrapperContainsChannel(convos, diffConvo));
+        assertTrue(conversationWrapperContainsChannel(convos, channel2));
+    }
+
+    @Test
+    public void testGetConversations_onlyImportant() {
+        String convoId = "convo";
+        NotificationChannel messages =
+                new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(PKG_O, UID_O, messages, true, false);
+        NotificationChannel calls =
+                new NotificationChannel("calls", "Calls", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(PKG_O, UID_O, calls, true, false);
+        NotificationChannel p =
+                new NotificationChannel("p calls", "Calls", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(PKG_P, UID_P, p, true, false);
+
+        NotificationChannel channel =
+                new NotificationChannel("A person msgs", "messages from A", IMPORTANCE_DEFAULT);
+        channel.setConversationId(messages.getId(), convoId);
+        channel.setImportantConversation(true);
+        mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+
+        NotificationChannel diffConvo =
+                new NotificationChannel("B person msgs", "messages from B", IMPORTANCE_DEFAULT);
+        diffConvo.setConversationId(p.getId(), "different convo");
+        diffConvo.setImportantConversation(true);
+        mHelper.createNotificationChannel(PKG_P, UID_P, diffConvo, true, false);
+
+        NotificationChannel channel2 =
+                new NotificationChannel("A person calls", "calls from A", IMPORTANCE_DEFAULT);
+        channel2.setConversationId(calls.getId(), convoId);
+        mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false);
+
+        List<ConversationChannelWrapper> convos = mHelper.getConversations(true);
+
+        assertEquals(2, convos.size());
+        assertTrue(conversationWrapperContainsChannel(convos, channel));
+        assertTrue(conversationWrapperContainsChannel(convos, diffConvo));
+        assertFalse(conversationWrapperContainsChannel(convos, channel2));
+    }
+
+    private boolean conversationWrapperContainsChannel(List<ConversationChannelWrapper> list,
+            NotificationChannel expected) {
+        for (ConversationChannelWrapper ccw : list) {
+            if (ccw.getNotificationChannel().equals(expected)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Test
     public void testGetConversations_invalidPkg() {
         assertThat(mHelper.getConversations("bad", 1)).isEmpty();
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 7753a32..6a8917c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -20,7 +20,9 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
@@ -53,6 +55,8 @@
 import static org.mockito.Mockito.never;
 
 import android.app.ActivityManager.TaskSnapshot;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.IInterface;
@@ -65,12 +69,16 @@
 
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
+import com.google.common.truth.Truth;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+
 /**
  * Build/Install/Run:
  *  atest WmTests:RecentsAnimationControllerTest
@@ -330,6 +338,107 @@
         assertTrue(activity.shouldAnimate(TRANSIT_ACTIVITY_CLOSE));
     }
 
+    @Test
+    public void testRecentViewInFixedPortraitWhenTopAppInLandscape() {
+        mWm.mIsFixedRotationTransformEnabled = true;
+        mWm.setRecentsAnimationController(mController);
+
+        final ActivityStack homeStack = mDisplayContent.getOrCreateStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+        final ActivityRecord homeAppWindow =
+                new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+                        .setStack(homeStack)
+                        .setCreateTask(true)
+                        .build();
+        final ActivityRecord appWindow = createActivityRecord(mDisplayContent,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        final WindowState win0 = createWindow(null, TYPE_BASE_APPLICATION, appWindow, "win1");
+        appWindow.addWindow(win0);
+
+        final ActivityRecord landActivity = createActivityRecord(mDisplayContent,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        landActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+        final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, landActivity, "win1");
+        landActivity.addWindow(win1);
+
+        assertEquals(landActivity.getTask().getTopVisibleActivity(), landActivity);
+        assertEquals(landActivity.findMainWindow(), win1);
+
+        // Ensure that the display is in Landscape
+        landActivity.onDescendantOrientationChanged(landActivity.token, landActivity);
+        assertEquals(Configuration.ORIENTATION_LANDSCAPE,
+                mDisplayContent.getConfiguration().orientation);
+
+        mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray(), homeAppWindow);
+
+        // Check that the home app is in portrait
+        assertEquals(Configuration.ORIENTATION_PORTRAIT,
+                homeAppWindow.getConfiguration().orientation);
+    }
+
+    @Test
+    public void testWallpaperHasFixedRotationApplied() {
+        mWm.mIsFixedRotationTransformEnabled = true;
+        mWm.setRecentsAnimationController(mController);
+
+        // Create a portrait home stack, a wallpaper and a landscape application displayed on top.
+
+        // Home stack
+        final ActivityStack homeStack = mDisplayContent.getOrCreateStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+        final ActivityRecord homeActivity =
+                new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+                        .setStack(homeStack)
+                        .setCreateTask(true)
+                        .build();
+        homeActivity.setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+
+        final WindowState homeWindow = createWindow(null, TYPE_BASE_APPLICATION, homeActivity,
+                "homeWindow");
+        homeActivity.addWindow(homeWindow);
+        homeWindow.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
+
+        // Landscape application
+        final ActivityRecord activity = createActivityRecord(mDisplayContent,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        final WindowState applicationWindow = createWindow(null, TYPE_BASE_APPLICATION, activity,
+                "applicationWindow");
+        activity.addWindow(applicationWindow);
+        activity.setOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+
+        // Wallpaper
+        final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+                mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
+        final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
+                "wallpaperWindow");
+
+        // Make sure the landscape activity is on top and the display is in landscape
+        activity.moveFocusableActivityToTop("test");
+        mDisplayContent.getConfiguration().windowConfiguration.setRotation(
+                mDisplayContent.getRotation());
+
+
+        spyOn(mDisplayContent.mWallpaperController);
+        doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
+
+        // Start the recents animation
+        mController
+                .initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray(), homeActivity);
+
+        mDisplayContent.mWallpaperController.adjustWallpaperWindows();
+
+        // Check preconditions
+        ArrayList<WallpaperWindowToken> wallpapers = new ArrayList<>(1);
+        mDisplayContent.forAllWallpaperWindows(wallpapers::add);
+
+        Truth.assertThat(wallpapers).hasSize(1);
+        Truth.assertThat(wallpapers.get(0).getTopChild()).isEqualTo(wallpaperWindow);
+
+        // Actual check
+        assertEquals(Configuration.ORIENTATION_PORTRAIT,
+                wallpapers.get(0).getConfiguration().orientation);
+    }
+
     private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
         verify(binder, atLeast(0)).asBinder();
         verifyNoMoreInteractions(binder);
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index b5663bd..851b052 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -260,4 +260,9 @@
     public SurfaceControl.Transaction unsetColor(SurfaceControl sc) {
         return this;
     }
+
+    @Override
+    public SurfaceControl.Transaction setShadowRadius(SurfaceControl sc, float shadowRadius) {
+        return this;
+    }
 }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 0886975..220cdce 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -2054,14 +2054,15 @@
         assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
-        // Bring up wifi with a score of 70.
+        // Bring up validated wifi.
         // Cell is lingered because it would not satisfy any request, even if it validated.
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
-        mWiFiNetworkAgent.adjustScore(50);
-        mWiFiNetworkAgent.connect(false);   // Score: 70
+        mWiFiNetworkAgent.connect(true);   // Score: 60
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        // TODO: Investigate sending validated before losing.
         callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
-        defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+        defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
@@ -5845,7 +5846,7 @@
 
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
-        trustedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+        trustedCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
         verify(mNetworkManagementService).setDefaultNetId(eq(mWiFiNetworkAgent.getNetwork().netId));
         reset(mNetworkManagementService);
 
diff --git a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
index a6b371a..d2532c2 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
+++ b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
@@ -16,14 +16,18 @@
 
 package com.android.server.connectivity
 
+import android.net.ConnectivityManager.TYPE_WIFI
+import android.net.LinkProperties
+import android.net.Network
+import android.net.NetworkAgentConfig
 import android.net.NetworkCapabilities
+import android.net.NetworkInfo
 import android.net.NetworkRequest
+import android.net.NetworkScore
 import androidx.test.filters.SmallTest
 import androidx.test.runner.AndroidJUnit4
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.Mockito.doReturn
 import org.mockito.Mockito.mock
 import kotlin.test.assertEquals
 import kotlin.test.assertNull
@@ -33,10 +37,24 @@
 class NetworkRankerTest {
     private val ranker = NetworkRanker()
 
-    private fun makeNai(satisfy: Boolean, score: Int) = mock(NetworkAgentInfo::class.java).also {
-        doReturn(satisfy).`when`(it).satisfies(any())
-        doReturn(score).`when`(it).currentScore
-        it.networkCapabilities = NetworkCapabilities()
+    private fun makeNai(satisfy: Boolean, score: Int) = object : NetworkAgentInfo(
+            null /* messenger */,
+            null /* asyncChannel*/,
+            Network(100),
+            NetworkInfo(TYPE_WIFI, 0 /* subtype */, "" /* typename */, "" /* subtypename */),
+            LinkProperties(),
+            NetworkCapabilities(),
+            NetworkScore.Builder().setLegacyScore(score).build(),
+            null /* context */,
+            null /* handler */,
+            NetworkAgentConfig(),
+            null /* connectivityService */,
+            null /* netd */,
+            null /* dnsResolver */,
+            null /* networkManagementService */,
+            0 /* factorySerialNumber */) {
+        override fun satisfies(request: NetworkRequest?): Boolean = satisfy
+        override fun getCurrentScore(): Int = score
     }
 
     @Test
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 1c330e2..3025caf 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -125,6 +125,8 @@
 
     DhcpInfo getDhcpInfo();
 
+    void setScanAlwaysAvailable(boolean isAvailable);
+
     boolean isScanAlwaysAvailable();
 
     boolean acquireWifiLock(IBinder lock, int lockType, String tag, in WorkSource ws);
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 0db3313..ae5bf7d 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -17,6 +17,7 @@
 package android.net.wifi;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -202,6 +203,11 @@
     private final List<MacAddress> mAllowedClientList;
 
     /**
+     * Whether auto shutdown of soft AP is enabled or not.
+     */
+    private final boolean mAutoShutdownEnabled;
+
+    /**
      * Delay in milliseconds before shutting down soft AP when
      * there are no connected devices.
      */
@@ -240,9 +246,9 @@
     /** Private constructor for Builder and Parcelable implementation. */
     private SoftApConfiguration(@Nullable String ssid, @Nullable MacAddress bssid,
             @Nullable String passphrase, boolean hiddenSsid, @BandType int band, int channel,
-            @SecurityType int securityType, int maxNumberOfClients, int shutdownTimeoutMillis,
-            boolean clientControlByUser, @NonNull List<MacAddress> blockedList,
-            @NonNull List<MacAddress> allowedList) {
+            @SecurityType int securityType, int maxNumberOfClients, boolean shutdownTimeoutEnabled,
+            int shutdownTimeoutMillis, boolean clientControlByUser,
+            @NonNull List<MacAddress> blockedList, @NonNull List<MacAddress> allowedList) {
         mSsid = ssid;
         mBssid = bssid;
         mPassphrase = passphrase;
@@ -251,6 +257,7 @@
         mChannel = channel;
         mSecurityType = securityType;
         mMaxNumberOfClients = maxNumberOfClients;
+        mAutoShutdownEnabled = shutdownTimeoutEnabled;
         mShutdownTimeoutMillis = shutdownTimeoutMillis;
         mClientControlByUser = clientControlByUser;
         mBlockedClientList = new ArrayList<>(blockedList);
@@ -274,6 +281,7 @@
                 && mChannel == other.mChannel
                 && mSecurityType == other.mSecurityType
                 && mMaxNumberOfClients == other.mMaxNumberOfClients
+                && mAutoShutdownEnabled == other.mAutoShutdownEnabled
                 && mShutdownTimeoutMillis == other.mShutdownTimeoutMillis
                 && mClientControlByUser == other.mClientControlByUser
                 && Objects.equals(mBlockedClientList, other.mBlockedClientList)
@@ -283,8 +291,9 @@
     @Override
     public int hashCode() {
         return Objects.hash(mSsid, mBssid, mPassphrase, mHiddenSsid,
-                mBand, mChannel, mSecurityType, mMaxNumberOfClients, mShutdownTimeoutMillis,
-                mClientControlByUser, mBlockedClientList, mAllowedClientList);
+                mBand, mChannel, mSecurityType, mMaxNumberOfClients, mAutoShutdownEnabled,
+                mShutdownTimeoutMillis, mClientControlByUser, mBlockedClientList,
+                mAllowedClientList);
     }
 
     @Override
@@ -299,6 +308,7 @@
         sbuf.append(" \n Channel =").append(mChannel);
         sbuf.append(" \n SecurityType=").append(getSecurityType());
         sbuf.append(" \n MaxClient=").append(mMaxNumberOfClients);
+        sbuf.append(" \n AutoShutdownEnabled=").append(mAutoShutdownEnabled);
         sbuf.append(" \n ShutdownTimeoutMillis=").append(mShutdownTimeoutMillis);
         sbuf.append(" \n ClientControlByUser=").append(mClientControlByUser);
         sbuf.append(" \n BlockedClientList=").append(mBlockedClientList);
@@ -316,6 +326,7 @@
         dest.writeInt(mChannel);
         dest.writeInt(mSecurityType);
         dest.writeInt(mMaxNumberOfClients);
+        dest.writeBoolean(mAutoShutdownEnabled);
         dest.writeInt(mShutdownTimeoutMillis);
         dest.writeBoolean(mClientControlByUser);
         dest.writeTypedList(mBlockedClientList);
@@ -335,7 +346,7 @@
                     in.readString(),
                     in.readParcelable(MacAddress.class.getClassLoader()),
                     in.readString(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt(),
-                    in.readInt(), in.readInt(), in.readBoolean(),
+                    in.readInt(), in.readBoolean(), in.readInt(), in.readBoolean(),
                     in.createTypedArrayList(MacAddress.CREATOR),
                     in.createTypedArrayList(MacAddress.CREATOR));
         }
@@ -429,6 +440,18 @@
     }
 
     /**
+     * Returns whether auto shutdown is enabled or not.
+     * The Soft AP will shutdown when there are no devices associated to it for
+     * the timeout duration. See {@link Builder#setAutoShutdownEnabled(boolean)}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean isAutoShutdownEnabled() {
+        return mAutoShutdownEnabled;
+    }
+
+    /**
      * Returns the shutdown timeout in milliseconds.
      * The Soft AP will shutdown when there are no devices associated to it for
      * the timeout duration. See {@link Builder#setShutdownTimeoutMillis(int)}.
@@ -551,6 +574,7 @@
         private int mChannel;
         private int mMaxNumberOfClients;
         private int mSecurityType;
+        private boolean mAutoShutdownEnabled;
         private int mShutdownTimeoutMillis;
         private boolean mClientControlByUser;
         private List<MacAddress> mBlockedClientList;
@@ -568,6 +592,7 @@
             mChannel = 0;
             mMaxNumberOfClients = 0;
             mSecurityType = SECURITY_TYPE_OPEN;
+            mAutoShutdownEnabled = true; // enabled by default.
             mShutdownTimeoutMillis = 0;
             mClientControlByUser = false;
             mBlockedClientList = new ArrayList<>();
@@ -588,6 +613,7 @@
             mChannel = other.mChannel;
             mMaxNumberOfClients = other.mMaxNumberOfClients;
             mSecurityType = other.mSecurityType;
+            mAutoShutdownEnabled = other.mAutoShutdownEnabled;
             mShutdownTimeoutMillis = other.mShutdownTimeoutMillis;
             mClientControlByUser = other.mClientControlByUser;
             mBlockedClientList = new ArrayList<>(other.mBlockedClientList);
@@ -603,8 +629,8 @@
         public SoftApConfiguration build() {
             return new SoftApConfiguration(mSsid, mBssid, mPassphrase,
                     mHiddenSsid, mBand, mChannel, mSecurityType, mMaxNumberOfClients,
-                    mShutdownTimeoutMillis, mClientControlByUser, mBlockedClientList,
-                    mAllowedClientList);
+                    mAutoShutdownEnabled, mShutdownTimeoutMillis, mClientControlByUser,
+                    mBlockedClientList, mAllowedClientList);
         }
 
         /**
@@ -789,7 +815,7 @@
          * @return Builder for chaining.
          */
         @NonNull
-        public Builder setMaxNumberOfClients(int maxNumberOfClients) {
+        public Builder setMaxNumberOfClients(@IntRange(from = 0) int maxNumberOfClients) {
             if (maxNumberOfClients < 0) {
                 throw new IllegalArgumentException("maxNumberOfClients should be not negative");
             }
@@ -798,6 +824,25 @@
         }
 
         /**
+         * Specifies whether auto shutdown is enabled or not.
+         * The Soft AP will shut down when there are no devices connected to it for
+         * the timeout duration.
+         *
+         * <p>
+         * <li>If not set, defaults to true</li>
+         *
+         * @param enable true to enable, false to disable.
+         * @return Builder for chaining.
+         *
+         * @see #setShutdownTimeoutMillis(int)
+         */
+        @NonNull
+        public Builder setAutoShutdownEnabled(boolean enable) {
+            mAutoShutdownEnabled = enable;
+            return this;
+        }
+
+        /**
          * Specifies the shutdown timeout in milliseconds.
          * The Soft AP will shut down when there are no devices connected to it for
          * the timeout duration.
@@ -807,14 +852,16 @@
          *
          * <p>
          * <li>If not set, defaults to 0</li>
-         * <li>The shut down timout will apply when
-         * {@link Settings.Global.SOFT_AP_TIMEOUT_ENABLED} is true</li>
+         * <li>The shut down timeout will apply when {@link #setAutoShutdownEnabled(boolean)} is
+         * set to true</li>
          *
          * @param timeoutMillis milliseconds of the timeout delay.
          * @return Builder for chaining.
+         *
+         * @see #setAutoShutdownEnabled(boolean)
          */
         @NonNull
-        public Builder setShutdownTimeoutMillis(int timeoutMillis) {
+        public Builder setShutdownTimeoutMillis(@IntRange(from = 0) int timeoutMillis) {
             if (timeoutMillis < 0) {
                 throw new IllegalArgumentException("Invalid timeout value");
             }
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index f693315..af2f246 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2755,6 +2755,26 @@
     }
 
     /**
+     * Set if scanning is always available.
+     *
+     * If set to {@code true}, apps can issue {@link #startScan} and fetch scan results
+     * even when Wi-Fi is turned off.
+     *
+     * @param isAvailable true to enable, false to disable.
+     * @hide
+     * @see #isScanAlwaysAvailable()
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public void setScanAlwaysAvailable(boolean isAvailable) {
+        try {
+            mService.setScanAlwaysAvailable(isAvailable);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Check if scanning is always available.
      *
      * If this return {@code true}, apps can issue {@link #startScan} and fetch scan results
diff --git a/wifi/java/android/net/wifi/WifiOemMigrationHook.java b/wifi/java/android/net/wifi/WifiOemMigrationHook.java
index 22d7786..44dbb98 100755
--- a/wifi/java/android/net/wifi/WifiOemMigrationHook.java
+++ b/wifi/java/android/net/wifi/WifiOemMigrationHook.java
@@ -21,8 +21,10 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.content.Context;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.provider.Settings;
 
 import java.util.List;
 
@@ -33,6 +35,9 @@
  */
 @SystemApi
 public final class WifiOemMigrationHook {
+
+    private WifiOemMigrationHook() { }
+
     /**
      * Container for all the wifi config data to migrate.
      */
@@ -152,8 +157,6 @@
         }
     }
 
-    private WifiOemMigrationHook() { }
-
     /**
      * Load data from OEM's config store.
      * <p>
@@ -178,4 +181,263 @@
         // Note: OEM's should add code to parse data from their config store format here!
         return null;
     }
+
+    /**
+     * Container for all the wifi settings data to migrate.
+     */
+    public static final class SettingsMigrationData implements Parcelable {
+        private final boolean mScanAlwaysAvailable;
+        private final boolean mP2pFactoryResetPending;
+        private final String mP2pDeviceName;
+        private final boolean mSoftApTimeoutEnabled;
+        private final boolean mWakeupEnabled;
+        private final boolean mScanThrottleEnabled;
+        private final boolean mVerboseLoggingEnabled;
+
+        private SettingsMigrationData(boolean scanAlwaysAvailable, boolean p2pFactoryResetPending,
+                @Nullable String p2pDeviceName, boolean softApTimeoutEnabled, boolean wakeupEnabled,
+                boolean scanThrottleEnabled, boolean verboseLoggingEnabled) {
+            mScanAlwaysAvailable = scanAlwaysAvailable;
+            mP2pFactoryResetPending = p2pFactoryResetPending;
+            mP2pDeviceName = p2pDeviceName;
+            mSoftApTimeoutEnabled = softApTimeoutEnabled;
+            mWakeupEnabled = wakeupEnabled;
+            mScanThrottleEnabled = scanThrottleEnabled;
+            mVerboseLoggingEnabled = verboseLoggingEnabled;
+        }
+
+        public static final @NonNull Parcelable.Creator<SettingsMigrationData> CREATOR =
+                new Parcelable.Creator<SettingsMigrationData>() {
+                    @Override
+                    public SettingsMigrationData createFromParcel(Parcel in) {
+                        boolean scanAlwaysAvailable = in.readBoolean();
+                        boolean p2pFactoryResetPending = in.readBoolean();
+                        String p2pDeviceName = in.readString();
+                        boolean softApTimeoutEnabled = in.readBoolean();
+                        boolean wakeupEnabled = in.readBoolean();
+                        boolean scanThrottleEnabled = in.readBoolean();
+                        boolean verboseLoggingEnabled = in.readBoolean();
+                        return new SettingsMigrationData(
+                                scanAlwaysAvailable, p2pFactoryResetPending,
+                                p2pDeviceName, softApTimeoutEnabled, wakeupEnabled,
+                                scanThrottleEnabled, verboseLoggingEnabled);
+                    }
+
+                    @Override
+                    public SettingsMigrationData[] newArray(int size) {
+                        return new SettingsMigrationData[size];
+                    }
+                };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeBoolean(mScanAlwaysAvailable);
+            dest.writeBoolean(mP2pFactoryResetPending);
+            dest.writeString(mP2pDeviceName);
+            dest.writeBoolean(mSoftApTimeoutEnabled);
+            dest.writeBoolean(mWakeupEnabled);
+            dest.writeBoolean(mScanThrottleEnabled);
+            dest.writeBoolean(mVerboseLoggingEnabled);
+        }
+
+        /**
+         * @return True if scans are allowed even when wifi is toggled off, false otherwise.
+         */
+        public boolean isScanAlwaysAvailable() {
+            return mScanAlwaysAvailable;
+        }
+
+        /**
+         * @return indicate whether factory reset request is pending.
+         */
+        public boolean isP2pFactoryResetPending() {
+            return mP2pFactoryResetPending;
+        }
+
+        /**
+         * @return the Wi-Fi peer-to-peer device name
+         */
+        public @Nullable String getP2pDeviceName() {
+            return mP2pDeviceName;
+        }
+
+        /**
+         * @return Whether soft AP will shut down after a timeout period when no devices are
+         * connected.
+         */
+        public boolean isSoftApTimeoutEnabled() {
+            return mSoftApTimeoutEnabled;
+        }
+
+        /**
+         * @return whether Wi-Fi Wakeup feature is enabled.
+         */
+        public boolean isWakeUpEnabled() {
+            return mWakeupEnabled;
+        }
+
+        /**
+         * @return Whether wifi scan throttle is enabled or not.
+         */
+        public boolean isScanThrottleEnabled() {
+            return mScanThrottleEnabled;
+        }
+
+        /**
+         * @return Whether to enable verbose logging in Wi-Fi.
+         */
+        public boolean isVerboseLoggingEnabled() {
+            return mVerboseLoggingEnabled;
+        }
+
+        /**
+         * Builder to create instance of {@link SettingsMigrationData}.
+         */
+        public static final class Builder {
+            private boolean mScanAlwaysAvailable;
+            private boolean mP2pFactoryResetPending;
+            private String mP2pDeviceName;
+            private boolean mSoftApTimeoutEnabled;
+            private boolean mWakeupEnabled;
+            private boolean mScanThrottleEnabled;
+            private boolean mVerboseLoggingEnabled;
+
+            public Builder() {
+            }
+
+            /**
+             * Setting to allow scans even when wifi is toggled off.
+             *
+             * @param available true if available, false otherwise.
+             * @return Instance of {@link Builder} to enable chaining of the builder method.
+             */
+            public @NonNull Builder setScanAlwaysAvailable(boolean available) {
+                mScanAlwaysAvailable = available;
+                return this;
+            }
+
+            /**
+             * Indicate whether factory reset request is pending.
+             *
+             * @param pending true if pending, false otherwise.
+             * @return Instance of {@link Builder} to enable chaining of the builder method.
+             */
+            public @NonNull Builder setP2pFactoryResetPending(boolean pending) {
+                mP2pFactoryResetPending = pending;
+                return this;
+            }
+
+            /**
+             * The Wi-Fi peer-to-peer device name
+             *
+             * @param name Name if set, null otherwise.
+             * @return Instance of {@link Builder} to enable chaining of the builder method.
+             */
+            public @NonNull Builder setP2pDeviceName(@Nullable String name) {
+                mP2pDeviceName = name;
+                return this;
+            }
+
+            /**
+             * Whether soft AP will shut down after a timeout period when no devices are connected.
+             *
+             * @param enabled true if enabled, false otherwise.
+             * @return Instance of {@link Builder} to enable chaining of the builder method.
+             */
+            public @NonNull Builder setSoftApTimeoutEnabled(boolean enabled) {
+                mSoftApTimeoutEnabled = enabled;
+                return this;
+            }
+
+            /**
+             * Value to specify if Wi-Fi Wakeup feature is enabled.
+             *
+             * @param enabled true if enabled, false otherwise.
+             * @return Instance of {@link Builder} to enable chaining of the builder method.
+             */
+            public @NonNull Builder setWakeUpEnabled(boolean enabled) {
+                mWakeupEnabled = enabled;
+                return this;
+            }
+
+            /**
+             * Whether wifi scan throttle is enabled or not.
+             *
+             * @param enabled true if enabled, false otherwise.
+             * @return Instance of {@link Builder} to enable chaining of the builder method.
+             */
+            public @NonNull Builder setScanThrottleEnabled(boolean enabled) {
+                mScanThrottleEnabled = enabled;
+                return this;
+            }
+
+            /**
+             * Setting to enable verbose logging in Wi-Fi.
+             *
+             * @param enabled true if enabled, false otherwise.
+             * @return Instance of {@link Builder} to enable chaining of the builder method.
+             */
+            public @NonNull Builder setVerboseLoggingEnabled(boolean enabled) {
+                mVerboseLoggingEnabled = enabled;
+                return this;
+            }
+
+            /**
+             * Build an instance of {@link SettingsMigrationData}.
+             *
+             * @return Instance of {@link SettingsMigrationData}.
+             */
+            public @NonNull SettingsMigrationData build() {
+                return new SettingsMigrationData(mScanAlwaysAvailable, mP2pFactoryResetPending,
+                        mP2pDeviceName, mSoftApTimeoutEnabled, mWakeupEnabled, mScanThrottleEnabled,
+                        mVerboseLoggingEnabled);
+            }
+        }
+    }
+
+    /**
+     * Load data from Settings.Global values.
+     *
+     * <p>
+     * Note:
+     * <li> This is method is invoked once on the first bootup. OEM can safely delete these settings
+     * once the migration is complete. The first & only relevant invocation of
+     * {@link #loadFromSettings(Context)} ()} occurs when a previously released
+     * device upgrades to the wifi mainline module from an OEM implementation of the wifi stack.
+     * </li>
+     *
+     * @param context Context to use for loading the settings provider.
+     * @return Instance of {@link SettingsMigrationData} for migrating data.
+     */
+    @NonNull
+    public static SettingsMigrationData loadFromSettings(@NonNull Context context) {
+        return new SettingsMigrationData.Builder()
+                .setScanAlwaysAvailable(
+                        Settings.Global.getInt(context.getContentResolver(),
+                                Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1)
+                .setP2pFactoryResetPending(
+                        Settings.Global.getInt(context.getContentResolver(),
+                                Settings.Global.WIFI_P2P_PENDING_FACTORY_RESET, 0) == 1)
+                .setP2pDeviceName(
+                        Settings.Global.getString(context.getContentResolver(),
+                                Settings.Global.WIFI_P2P_DEVICE_NAME))
+                .setSoftApTimeoutEnabled(
+                        Settings.Global.getInt(context.getContentResolver(),
+                                Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1) == 1)
+                .setWakeUpEnabled(
+                        Settings.Global.getInt(context.getContentResolver(),
+                                Settings.Global.WIFI_WAKEUP_ENABLED, 0) == 1)
+                .setScanThrottleEnabled(
+                        Settings.Global.getInt(context.getContentResolver(),
+                                Settings.Global.WIFI_SCAN_THROTTLE_ENABLED, 1) == 1)
+                .setVerboseLoggingEnabled(
+                        Settings.Global.getInt(context.getContentResolver(),
+                                Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, 1) == 1)
+                .build();
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
index d958488..060ddf0 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -125,6 +125,7 @@
                 .setChannel(149, SoftApConfiguration.BAND_5GHZ)
                 .setHiddenSsid(true)
                 .setMaxNumberOfClients(10)
+                .setAutoShutdownEnabled(true)
                 .setShutdownTimeoutMillis(500000)
                 .enableClientControlByUser(true)
                 .setClientList(testBlockedClientList, testAllowedClientList)
@@ -136,6 +137,7 @@
         assertThat(original.getChannel()).isEqualTo(149);
         assertThat(original.isHiddenSsid()).isEqualTo(true);
         assertThat(original.getMaxNumberOfClients()).isEqualTo(10);
+        assertThat(original.isAutoShutdownEnabled()).isEqualTo(true);
         assertThat(original.getShutdownTimeoutMillis()).isEqualTo(500000);
         assertThat(original.isClientControlByUserEnabled()).isEqualTo(true);
         assertThat(original.getBlockedClientList()).isEqualTo(testBlockedClientList);
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 853212a..234d929 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -2401,4 +2401,15 @@
         assertFalse(mWifiManager.isAutoWakeupEnabled());
         verify(mWifiService).isAutoWakeupEnabled();
     }
+
+
+    @Test
+    public void testScanAvailable() throws Exception {
+        mWifiManager.setScanAlwaysAvailable(true);
+        verify(mWifiService).setScanAlwaysAvailable(true);
+
+        when(mWifiService.isScanAlwaysAvailable()).thenReturn(false);
+        assertFalse(mWifiManager.isScanAlwaysAvailable());
+        verify(mWifiService).isScanAlwaysAvailable();
+    }
 }