Merge "Remove unused Media 2.0 APIs"
diff --git a/Android.bp b/Android.bp
index c672315..01dfe30 100644
--- a/Android.bp
+++ b/Android.bp
@@ -254,6 +254,7 @@
         ":statsd_aidl",
         "core/java/android/os/ISystemUpdateManager.aidl",
         "core/java/android/os/IThermalEventListener.aidl",
+        "core/java/android/os/IThermalStatusListener.aidl",
         "core/java/android/os/IThermalService.aidl",
         "core/java/android/os/IUpdateLock.aidl",
         "core/java/android/os/IUserManager.aidl",
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index ec3366c..5936ee4 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -10,3 +10,5 @@
 ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
 
 owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$"
+
+shell_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^packages/Shell/"
diff --git a/api/current.txt b/api/current.txt
old mode 100755
new mode 100644
index ff78d48..edd37ab
--- a/api/current.txt
+++ b/api/current.txt
@@ -7307,7 +7307,10 @@
     method public android.content.Intent createRequestRoleIntent(java.lang.String);
     method public boolean isRoleAvailable(java.lang.String);
     method public boolean isRoleHeld(java.lang.String);
+    field public static final java.lang.String ROLE_BROWSER = "android.app.role.BROWSER";
     field public static final java.lang.String ROLE_DIALER = "android.app.role.DIALER";
+    field public static final java.lang.String ROLE_GALLERY = "android.app.role.GALLERY";
+    field public static final java.lang.String ROLE_MUSIC = "android.app.role.MUSIC";
     field public static final java.lang.String ROLE_SMS = "android.app.role.SMS";
   }
 
@@ -9598,6 +9601,7 @@
     method public abstract void unbindService(android.content.ServiceConnection);
     method public void unregisterComponentCallbacks(android.content.ComponentCallbacks);
     method public abstract void unregisterReceiver(android.content.BroadcastReceiver);
+    method public abstract void updateServiceGroup(android.content.ServiceConnection, int, int);
     field public static final java.lang.String ACCESSIBILITY_SERVICE = "accessibility";
     field public static final java.lang.String ACCOUNT_SERVICE = "account";
     field public static final java.lang.String ACTIVITY_SERVICE = "activity";
@@ -9796,6 +9800,7 @@
     method public boolean stopService(android.content.Intent);
     method public void unbindService(android.content.ServiceConnection);
     method public void unregisterReceiver(android.content.BroadcastReceiver);
+    method public void updateServiceGroup(android.content.ServiceConnection, int, int);
   }
 
   public deprecated class CursorLoader extends android.content.AsyncTaskLoader {
@@ -34630,14 +34635,14 @@
 
 package android.preference {
 
-  public class CheckBoxPreference extends android.preference.TwoStatePreference {
+  public deprecated class CheckBoxPreference extends android.preference.TwoStatePreference {
     ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet, int);
     ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet, int, int);
     ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet);
     ctor public CheckBoxPreference(android.content.Context);
   }
 
-  public abstract class DialogPreference extends android.preference.Preference implements android.content.DialogInterface.OnClickListener android.content.DialogInterface.OnDismissListener android.preference.PreferenceManager.OnActivityDestroyListener {
+  public abstract deprecated class DialogPreference extends android.preference.Preference implements android.content.DialogInterface.OnClickListener android.content.DialogInterface.OnDismissListener android.preference.PreferenceManager.OnActivityDestroyListener {
     ctor public DialogPreference(android.content.Context, android.util.AttributeSet, int, int);
     ctor public DialogPreference(android.content.Context, android.util.AttributeSet, int);
     ctor public DialogPreference(android.content.Context, android.util.AttributeSet);
@@ -34670,7 +34675,7 @@
     method protected void showDialog(android.os.Bundle);
   }
 
-  public class EditTextPreference extends android.preference.DialogPreference {
+  public deprecated class EditTextPreference extends android.preference.DialogPreference {
     ctor public EditTextPreference(android.content.Context, android.util.AttributeSet, int, int);
     ctor public EditTextPreference(android.content.Context, android.util.AttributeSet, int);
     ctor public EditTextPreference(android.content.Context, android.util.AttributeSet);
@@ -34681,7 +34686,7 @@
     method public void setText(java.lang.String);
   }
 
-  public class ListPreference extends android.preference.DialogPreference {
+  public deprecated class ListPreference extends android.preference.DialogPreference {
     ctor public ListPreference(android.content.Context, android.util.AttributeSet, int, int);
     ctor public ListPreference(android.content.Context, android.util.AttributeSet, int);
     ctor public ListPreference(android.content.Context, android.util.AttributeSet);
@@ -34699,7 +34704,7 @@
     method public void setValueIndex(int);
   }
 
-  public class MultiSelectListPreference extends android.preference.DialogPreference {
+  public deprecated class MultiSelectListPreference extends android.preference.DialogPreference {
     ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet, int, int);
     ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet, int);
     ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet);
@@ -34715,7 +34720,7 @@
     method public void setValues(java.util.Set<java.lang.String>);
   }
 
-  public class Preference implements java.lang.Comparable {
+  public deprecated class Preference implements java.lang.Comparable {
     ctor public Preference(android.content.Context, android.util.AttributeSet, int, int);
     ctor public Preference(android.content.Context, android.util.AttributeSet, int);
     ctor public Preference(android.content.Context, android.util.AttributeSet);
@@ -34812,21 +34817,21 @@
     field public static final int DEFAULT_ORDER = 2147483647; // 0x7fffffff
   }
 
-  public static class Preference.BaseSavedState extends android.view.AbsSavedState {
+  public static deprecated class Preference.BaseSavedState extends android.view.AbsSavedState {
     ctor public Preference.BaseSavedState(android.os.Parcel);
     ctor public Preference.BaseSavedState(android.os.Parcelable);
     field public static final android.os.Parcelable.Creator<android.preference.Preference.BaseSavedState> CREATOR;
   }
 
-  public static abstract interface Preference.OnPreferenceChangeListener {
+  public static abstract deprecated interface Preference.OnPreferenceChangeListener {
     method public abstract boolean onPreferenceChange(android.preference.Preference, java.lang.Object);
   }
 
-  public static abstract interface Preference.OnPreferenceClickListener {
+  public static abstract deprecated interface Preference.OnPreferenceClickListener {
     method public abstract boolean onPreferenceClick(android.preference.Preference);
   }
 
-  public abstract class PreferenceActivity extends android.app.ListActivity implements android.preference.PreferenceFragment.OnPreferenceStartFragmentCallback {
+  public abstract deprecated class PreferenceActivity extends android.app.ListActivity implements android.preference.PreferenceFragment.OnPreferenceStartFragmentCallback {
     ctor public PreferenceActivity();
     method public deprecated void addPreferencesFromIntent(android.content.Intent);
     method public deprecated void addPreferencesFromResource(int);
@@ -34866,7 +34871,7 @@
     field public static final long HEADER_ID_UNDEFINED = -1L; // 0xffffffffffffffffL
   }
 
-  public static final class PreferenceActivity.Header implements android.os.Parcelable {
+  public static final deprecated class PreferenceActivity.Header implements android.os.Parcelable {
     ctor public PreferenceActivity.Header();
     method public int describeContents();
     method public java.lang.CharSequence getBreadCrumbShortTitle(android.content.res.Resources);
@@ -34892,14 +34897,14 @@
     field public int titleRes;
   }
 
-  public class PreferenceCategory extends android.preference.PreferenceGroup {
+  public deprecated class PreferenceCategory extends android.preference.PreferenceGroup {
     ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet, int, int);
     ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet, int);
     ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet);
     ctor public PreferenceCategory(android.content.Context);
   }
 
-  public abstract interface PreferenceDataStore {
+  public abstract deprecated interface PreferenceDataStore {
     method public default boolean getBoolean(java.lang.String, boolean);
     method public default float getFloat(java.lang.String, float);
     method public default int getInt(java.lang.String, int);
@@ -34929,7 +34934,7 @@
     method public abstract boolean onPreferenceStartFragment(android.preference.PreferenceFragment, android.preference.Preference);
   }
 
-  public abstract class PreferenceGroup extends android.preference.Preference {
+  public abstract deprecated class PreferenceGroup extends android.preference.Preference {
     ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet, int, int);
     ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet, int);
     ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet);
@@ -34948,7 +34953,7 @@
     method public void setOrderingAsAdded(boolean);
   }
 
-  public class PreferenceManager {
+  public deprecated class PreferenceManager {
     method public android.preference.PreferenceScreen createPreferenceScreen(android.content.Context);
     method public android.preference.Preference findPreference(java.lang.CharSequence);
     method public static android.content.SharedPreferences getDefaultSharedPreferences(android.content.Context);
@@ -34970,19 +34975,19 @@
     field public static final java.lang.String METADATA_KEY_PREFERENCES = "android.preference";
   }
 
-  public static abstract interface PreferenceManager.OnActivityDestroyListener {
+  public static abstract deprecated interface PreferenceManager.OnActivityDestroyListener {
     method public abstract void onActivityDestroy();
   }
 
-  public static abstract interface PreferenceManager.OnActivityResultListener {
+  public static abstract deprecated interface PreferenceManager.OnActivityResultListener {
     method public abstract boolean onActivityResult(int, int, android.content.Intent);
   }
 
-  public static abstract interface PreferenceManager.OnActivityStopListener {
+  public static abstract deprecated interface PreferenceManager.OnActivityStopListener {
     method public abstract void onActivityStop();
   }
 
-  public final class PreferenceScreen extends android.preference.PreferenceGroup implements android.widget.AdapterView.OnItemClickListener android.content.DialogInterface.OnDismissListener {
+  public final deprecated class PreferenceScreen extends android.preference.PreferenceGroup implements android.widget.AdapterView.OnItemClickListener android.content.DialogInterface.OnDismissListener {
     method public void bind(android.widget.ListView);
     method public android.app.Dialog getDialog();
     method public android.widget.ListAdapter getRootAdapter();
@@ -34991,7 +34996,7 @@
     method public void onItemClick(android.widget.AdapterView, android.view.View, int, long);
   }
 
-  public class RingtonePreference extends android.preference.Preference implements android.preference.PreferenceManager.OnActivityResultListener {
+  public deprecated class RingtonePreference extends android.preference.Preference implements android.preference.PreferenceManager.OnActivityResultListener {
     ctor public RingtonePreference(android.content.Context, android.util.AttributeSet, int, int);
     ctor public RingtonePreference(android.content.Context, android.util.AttributeSet, int);
     ctor public RingtonePreference(android.content.Context, android.util.AttributeSet);
@@ -35008,7 +35013,7 @@
     method public void setShowSilent(boolean);
   }
 
-  public class SwitchPreference extends android.preference.TwoStatePreference {
+  public deprecated class SwitchPreference extends android.preference.TwoStatePreference {
     ctor public SwitchPreference(android.content.Context, android.util.AttributeSet, int, int);
     ctor public SwitchPreference(android.content.Context, android.util.AttributeSet, int);
     ctor public SwitchPreference(android.content.Context, android.util.AttributeSet);
@@ -35021,7 +35026,7 @@
     method public void setSwitchTextOn(int);
   }
 
-  public abstract class TwoStatePreference extends android.preference.Preference {
+  public abstract deprecated class TwoStatePreference extends android.preference.Preference {
     ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet, int, int);
     ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet, int);
     ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet);
@@ -42856,6 +42861,7 @@
     field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
     field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_SETTING_BOOL = "editable_voicemail_number_setting_bool";
     field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
+    field public static final java.lang.String KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL = "enhanced_4g_lte_on_by_default_bool";
     field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
     field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
     field public static final java.lang.String KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY = "gsm_nonroaming_networks_string_array";
diff --git a/api/system-current.txt b/api/system-current.txt
index f013b6d..4d590a3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -75,6 +75,7 @@
     field public static final java.lang.String GET_PROCESS_STATE_AND_OOM_SCORE = "android.permission.GET_PROCESS_STATE_AND_OOM_SCORE";
     field public static final java.lang.String GET_TOP_ACTIVITY_INFO = "android.permission.GET_TOP_ACTIVITY_INFO";
     field public static final java.lang.String GLOBAL_SEARCH = "android.permission.GLOBAL_SEARCH";
+    field public static final java.lang.String GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS = "android.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS";
     field public static final java.lang.String GRANT_RUNTIME_PERMISSIONS = "android.permission.GRANT_RUNTIME_PERMISSIONS";
     field public static final java.lang.String HARDWARE_TEST = "android.permission.HARDWARE_TEST";
     field public static final java.lang.String HDMI_CEC = "android.permission.HDMI_CEC";
@@ -586,6 +587,7 @@
     method public boolean packageHasActiveAdmins(java.lang.String);
     method public deprecated boolean setActiveProfileOwner(android.content.ComponentName, java.lang.String) throws java.lang.IllegalArgumentException;
     method public void setDeviceProvisioningConfigApplied();
+    method public void setProfileOwnerCanAccessDeviceIdsForUser(android.content.ComponentName, android.os.UserHandle);
     field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED";
     field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED";
     field public static final java.lang.String ACTION_PROVISION_FINALIZATION = "android.app.action.PROVISION_FINALIZATION";
@@ -867,6 +869,7 @@
     method public java.util.List<java.lang.String> getRoleHoldersAsUser(java.lang.String, android.os.UserHandle);
     method public void removeRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
     method public boolean removeRoleHolderFromController(java.lang.String, java.lang.String);
+    method public void setRoleNamesFromController(java.util.List<java.lang.String>);
     field public static final java.lang.String EXTRA_REQUEST_ROLE_NAME = "android.app.role.extra.REQUEST_ROLE_NAME";
   }
 
@@ -4354,7 +4357,7 @@
 
 package android.preference {
 
-  public class PreferenceManager {
+  public deprecated class PreferenceManager {
     method public boolean isStorageCredentialProtected();
     method public void setStorageCredentialProtected();
   }
@@ -6251,6 +6254,45 @@
     field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
   }
 
+  public class ImsMmTelManager {
+    method public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(android.content.Context, int);
+    method public int getVoWiFiModeSetting();
+    method public boolean isAdvancedCallingSettingEnabled();
+    method public boolean isAvailable(int, int);
+    method public boolean isCapable(int, int);
+    method public boolean isVoWiFiRoamingSettingEnabled();
+    method public boolean isVoWiFiSettingEnabled();
+    method public boolean isVtSettingEnabled();
+    method public void registerImsRegistrationCallback(java.util.concurrent.Executor, android.telephony.ims.ImsMmTelManager.RegistrationCallback);
+    method public void registerMmTelCapabilityCallback(java.util.concurrent.Executor, android.telephony.ims.ImsMmTelManager.CapabilityCallback);
+    method public void setAdvancedCallingSetting(boolean);
+    method public void setRttCapabilitySetting(boolean);
+    method public void setVoWiFiModeSetting(int);
+    method public void setVoWiFiNonPersistent(boolean, int);
+    method public void setVoWiFiRoamingModeSetting(int);
+    method public void setVoWiFiRoamingSetting(boolean);
+    method public void setVoWiFiSetting(boolean);
+    method public void setVtSetting(boolean);
+    method public void unregisterImsRegistrationCallback(android.telephony.ims.ImsMmTelManager.RegistrationCallback);
+    method public void unregisterMmTelCapabilityCallback(android.telephony.ims.ImsMmTelManager.CapabilityCallback);
+    field public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; // 0x1
+    field public static final int WIFI_MODE_WIFI_ONLY = 0; // 0x0
+    field public static final int WIFI_MODE_WIFI_PREFERRED = 2; // 0x2
+  }
+
+  public static class ImsMmTelManager.CapabilityCallback {
+    ctor public ImsMmTelManager.CapabilityCallback();
+    method public void onCapabilitiesStatusChanged(android.telephony.ims.feature.MmTelFeature.MmTelCapabilities);
+  }
+
+  public static class ImsMmTelManager.RegistrationCallback {
+    ctor public ImsMmTelManager.RegistrationCallback();
+    method public void onDeregistered(android.telephony.ims.ImsReasonInfo);
+    method public void onRegistered(int);
+    method public void onRegistering(int);
+    method public void onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo);
+  }
+
   public final class ImsReasonInfo implements android.os.Parcelable {
     ctor public ImsReasonInfo(int, int, java.lang.String);
     method public int describeContents();
diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp
new file mode 100644
index 0000000..31bd612
--- /dev/null
+++ b/cmds/bootanimation/Android.bp
@@ -0,0 +1,90 @@
+cc_defaults {
+    name: "bootanimation_defaults",
+
+    cflags: [
+        "-DGL_GLEXT_PROTOTYPES",
+        "-DEGL_EGLEXT_PROTOTYPES",
+
+        "-Wall",
+        "-Werror",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+
+    shared_libs: [
+        "libandroidfw",
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "liblog",
+        "libutils",
+    ],
+}
+
+// bootanimation executable
+// =========================================================
+
+cc_binary {
+    name: "bootanimation",
+    defaults: ["bootanimation_defaults"],
+
+    shared_libs: [
+        "libOpenSLES",
+        "libbootanimation",
+    ],
+
+    srcs: [
+        "BootAnimationUtil.cpp",
+
+        "bootanimation_main.cpp",
+        "audioplay.cpp",
+    ],
+
+    product_variables: {
+        product_is_iot: {
+            shared_libs: [
+                "libandroidthings",
+                "libandroidthings_protos",
+                "libchrome",
+                "libprotobuf-cpp-lite",
+            ],
+            static_libs: ["libjsoncpp"],
+            srcs: [
+                "iot/iotbootanimation_main.cpp",
+                "iot/BootAction.cpp",
+                "iot/BootParameters.cpp",
+            ],
+            exclude_srcs: [
+                "bootanimation_main.cpp",
+                "audioplay.cpp",
+            ],
+        },
+    },
+
+    init_rc: ["bootanim.rc"],
+}
+
+// libbootanimation
+// ===========================================================
+
+cc_library_shared {
+    name: "libbootanimation",
+    defaults: ["bootanimation_defaults"],
+
+    srcs: ["BootAnimation.cpp"],
+
+    shared_libs: [
+        "libui",
+        "libhwui",
+        "libEGL",
+        "libGLESv1_CM",
+        "libgui",
+        "libtinyalsa",
+    ],
+
+    product_variables: {
+        product_is_iot: {
+            init_rc: ["iot/bootanim_iot.rc"],
+        },
+    },
+}
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
deleted file mode 100644
index 6943dab..0000000
--- a/cmds/bootanimation/Android.mk
+++ /dev/null
@@ -1,103 +0,0 @@
-bootanimation_CommonCFlags = -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
-bootanimation_CommonCFlags += -Wall -Werror -Wunused -Wunreachable-code
-
-
-# bootanimation executable
-# =========================================================
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_CFLAGS += ${bootanimation_CommonCFlags}
-
-LOCAL_SHARED_LIBRARIES := \
-    libOpenSLES \
-    libandroidfw \
-    libbase \
-    libbinder \
-    libbootanimation \
-    libcutils \
-    liblog \
-    libutils \
-
-LOCAL_SRC_FILES:= \
-    BootAnimationUtil.cpp \
-
-ifeq ($(PRODUCT_IOT),true)
-
-LOCAL_SHARED_LIBRARIES += \
-    libandroidthings \
-    libandroidthings_protos \
-    libchrome \
-    libprotobuf-cpp-lite \
-
-LOCAL_STATIC_LIBRARIES += \
-    libjsoncpp
-
-LOCAL_SRC_FILES += \
-    iot/iotbootanimation_main.cpp \
-    iot/BootAction.cpp \
-    iot/BootParameters.cpp \
-
-else
-
-LOCAL_SRC_FILES += \
-    bootanimation_main.cpp \
-    audioplay.cpp \
-
-endif  # PRODUCT_IOT
-
-LOCAL_MODULE:= bootanimation
-
-LOCAL_INIT_RC := bootanim.rc
-
-ifdef TARGET_32_BIT_SURFACEFLINGER
-LOCAL_32_BIT_ONLY := true
-endif
-
-include $(BUILD_EXECUTABLE)
-
-
-# libbootanimation
-# ===========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbootanimation
-LOCAL_CFLAGS += ${bootanimation_CommonCFlags}
-
-LOCAL_SRC_FILES:= \
-    BootAnimation.cpp
-
-LOCAL_CFLAGS += ${bootanimation_CommonCFlags}
-
-LOCAL_C_INCLUDES += \
-    external/tinyalsa/include \
-    frameworks/wilhelm/include
-
-LOCAL_SHARED_LIBRARIES := \
-    libcutils \
-    liblog \
-    libandroidfw \
-    libutils \
-    libbinder \
-    libui \
-    libhwui \
-    libEGL \
-    libGLESv1_CM \
-    libgui \
-    libtinyalsa \
-    libbase
-
-ifeq ($(PRODUCT_IOT),true)
-
-LOCAL_INIT_RC := iot/bootanim_iot.rc
-
-endif # PRODUCT_IOT
-
-ifdef TARGET_32_BIT_SURFACEFLINGER
-LOCAL_32_BIT_ONLY := true
-endif
-
-include ${BUILD_SHARED_LIBRARY}
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/cmds/bootanimation/iot/Android.bp b/cmds/bootanimation/iot/Android.bp
new file mode 100644
index 0000000..1f248ad
--- /dev/null
+++ b/cmds/bootanimation/iot/Android.bp
@@ -0,0 +1,49 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// libbootanimation_iot_test
+// ===========================================================
+cc_test {
+    name: "libbootanimation_iot_test",
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+
+    shared_libs: [
+        "libandroidthings",
+        "libandroidthings_protos",
+        "libbase",
+        "libchrome",
+        "liblog",
+        "libprotobuf-cpp-lite",
+    ],
+
+    static_libs: ["libjsoncpp"],
+
+    srcs: [
+        "BootParameters.cpp",
+        "BootParameters_test.cpp",
+    ],
+
+    enabled: false,
+    product_variables: {
+        product_is_iot: {
+            enabled: true,
+        },
+    },
+}
diff --git a/cmds/bootanimation/iot/Android.mk b/cmds/bootanimation/iot/Android.mk
deleted file mode 100644
index 3d288e4..0000000
--- a/cmds/bootanimation/iot/Android.mk
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-ifeq ($(PRODUCT_IOT),true)
-
-# libbootanimation_iot_test
-# ===========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbootanimation_iot_test
-LOCAL_CFLAGS := -Wall -Werror -Wunused -Wunreachable-code
-
-LOCAL_SHARED_LIBRARIES := \
-    libandroidthings \
-    libandroidthings_protos \
-    libbase \
-    libchrome \
-    liblog \
-    libprotobuf-cpp-lite \
-
-LOCAL_STATIC_LIBRARIES += \
-    libjsoncpp
-
-LOCAL_SRC_FILES := \
-    BootParameters.cpp \
-    BootParameters_test.cpp \
-
-include $(BUILD_NATIVE_TEST)
-
-endif # PRODUCT_IOT
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index 376b13c..6c6797a 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -48,6 +48,8 @@
     private static final String COMMAND_CLEAR_FREEZE_PERIOD_RECORD = "clear-freeze-period-record";
     private static final String COMMAND_FORCE_NETWORK_LOGS = "force-network-logs";
     private static final String COMMAND_FORCE_SECURITY_LOGS = "force-security-logs";
+    private static final String COMMAND_GRANT_PO_DEVICE_ID_ACCESS =
+            "grant-profile-owner-device-ids-access";
 
     private IDevicePolicyManager mDevicePolicyManager;
     private int mUserId = UserHandle.USER_SYSTEM;
@@ -89,7 +91,10 @@
                 "the DPC and triggers DeviceAdminReceiver.onNetworkLogsAvailable() if needed.\n" +
                 "\n" +
                 "dpm " + COMMAND_FORCE_SECURITY_LOGS + ": makes all security logs available to " +
-                "the DPC and triggers DeviceAdminReceiver.onSecurityLogsAvailable() if needed.");
+                "the DPC and triggers DeviceAdminReceiver.onSecurityLogsAvailable() if needed."
+                + "\n"
+                + "usage: dpm " + COMMAND_GRANT_PO_DEVICE_ID_ACCESS + ": "
+                + "[ --user <USER_ID> | current ] <COMPONENT>\n");
     }
 
     @Override
@@ -124,6 +129,9 @@
             case COMMAND_FORCE_SECURITY_LOGS:
                 runForceSecurityLogs();
                 break;
+            case COMMAND_GRANT_PO_DEVICE_ID_ACCESS:
+                runGrantProfileOwnerDeviceIdsAccess();
+                break;
             default:
                 throw new IllegalArgumentException ("unknown command '" + command + "'");
         }
@@ -242,6 +250,13 @@
         System.out.println("Success");
     }
 
+
+    private void runGrantProfileOwnerDeviceIdsAccess() throws RemoteException {
+        parseArgs(/*canHaveName=*/ false);
+        mDevicePolicyManager.grantDeviceIdsAccessToProfileOwner(mComponent, mUserId);
+        System.out.println("Success");
+    }
+
     private ComponentName parseComponentName(String component) {
         ComponentName cn = ComponentName.unflattenFromString(component);
         if (cn == null) {
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index 1191e6a..020c443 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -48,8 +48,6 @@
 using android::Res_value;
 using android::ResStringPool;
 using android::ResTable_config;
-using android::String16;
-using android::String8;
 using android::StringPiece16;
 using android::base::StringPrintf;
 using android::idmap2::CommandLineOptions;
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index e5764f0..2dcf50f 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -12274,7 +12274,7 @@
 HPLorg/ccil/cowan/tagsoup/AttributesImpl;->setAttribute(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
 HPLorg/ccil/cowan/tagsoup/Element;->anonymize()V
 HPLorg/ccil/cowan/tagsoup/Element;->preclose()V
-HPLorg/ccil/cowan/tagsoup/HTMLSchema;-><init>()V
+# HPLorg/ccil/cowan/tagsoup/HTMLSchema;-><init>()V b/76145463
 HPLorg/json/JSONArray;->optDouble(I)D
 HPLorg/json/JSONArray;->optDouble(ID)D
 HPLorg/json/JSONStringer;->value(J)Lorg/json/JSONStringer;
@@ -15623,7 +15623,7 @@
 HSPLandroid/app/admin/IDevicePolicyManager$Stub$Proxy;->isLockTaskPermitted(Ljava/lang/String;)Z
 HSPLandroid/app/admin/IDevicePolicyManager$Stub$Proxy;->isProvisioningAllowed(Ljava/lang/String;Ljava/lang/String;)Z
 HSPLandroid/app/admin/IDevicePolicyManager$Stub;-><init>()V
-HSPLandroid/app/admin/IDevicePolicyManager$Stub;->onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z
+# HSPLandroid/app/admin/IDevicePolicyManager$Stub;->onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z b/76145463
 HSPLandroid/app/admin/IDevicePolicyManager;->addCrossProfileIntentFilter(Landroid/content/ComponentName;Landroid/content/IntentFilter;I)V
 HSPLandroid/app/admin/IDevicePolicyManager;->addCrossProfileWidgetProvider(Landroid/content/ComponentName;Ljava/lang/String;)Z
 HSPLandroid/app/admin/IDevicePolicyManager;->addOverrideApn(Landroid/content/ComponentName;Landroid/telephony/data/ApnSetting;)I
@@ -25254,7 +25254,7 @@
 HSPLandroid/media/MediaCodecInfo$VideoCapabilities;->parseFromInfo(Landroid/media/MediaFormat;)V
 HSPLandroid/media/MediaCodecInfo$VideoCapabilities;->parseWidthHeightRanges(Ljava/lang/Object;)Landroid/util/Pair;
 HSPLandroid/media/MediaCodecInfo$VideoCapabilities;->supports(Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Number;)Z
-HSPLandroid/media/MediaCodecInfo$VideoCapabilities;->updateLimits()V
+# HSPLandroid/media/MediaCodecInfo$VideoCapabilities;->updateLimits()V b/76145463
 HSPLandroid/media/MediaCodecInfo;-><init>(Ljava/lang/String;Z[Landroid/media/MediaCodecInfo$CodecCapabilities;)V
 HSPLandroid/media/MediaCodecInfo;->access$200()Landroid/util/Range;
 HSPLandroid/media/MediaCodecInfo;->access$300()Landroid/util/Range;
@@ -37845,7 +37845,7 @@
 HSPLandroid/widget/ViewSwitcher;->getNextView()Landroid/view/View;
 HSPLandroid/widget/WrapperListAdapter;->getWrappedAdapter()Landroid/widget/ListAdapter;
 HSPLcom/android/i18n/phonenumbers/AlternateFormatsCountryCodeSet;->getCountryCodeSet()Ljava/util/Set;
-HSPLcom/android/i18n/phonenumbers/CountryCodeToRegionCodeMap;->getCountryCodeToRegionCodeMap()Ljava/util/Map;
+# HSPLcom/android/i18n/phonenumbers/CountryCodeToRegionCodeMap;->getCountryCodeToRegionCodeMap()Ljava/util/Map; b/76145463
 HSPLcom/android/i18n/phonenumbers/MetadataLoader;->loadMetadata(Ljava/lang/String;)Ljava/io/InputStream;
 HSPLcom/android/i18n/phonenumbers/MetadataManager$1;-><init>()V
 HSPLcom/android/i18n/phonenumbers/MetadataManager$1;->loadMetadata(Ljava/lang/String;)Ljava/io/InputStream;
@@ -37999,7 +37999,7 @@
 HSPLcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->setCountryCodeSource(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;)Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
 HSPLcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->setNationalNumber(J)Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
 HSPLcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->setRawInput(Ljava/lang/String;)Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
-HSPLcom/android/i18n/phonenumbers/ShortNumbersRegionCodeSet;->getRegionCodeSet()Ljava/util/Set;
+# HSPLcom/android/i18n/phonenumbers/ShortNumbersRegionCodeSet;->getRegionCodeSet()Ljava/util/Set; b/76145463
 HSPLcom/android/i18n/phonenumbers/internal/MatcherApi;->matchNationalNumber(Ljava/lang/CharSequence;Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;Z)Z
 HSPLcom/android/i18n/phonenumbers/internal/RegexBasedMatcher;->matchNationalNumber(Ljava/lang/CharSequence;Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;Z)Z
 HSPLcom/android/i18n/phonenumbers/internal/RegexCache$LRUCache$1;->removeEldestEntry(Ljava/util/Map$Entry;)Z
@@ -38978,7 +38978,7 @@
 HSPLcom/android/internal/os/BatteryStatsImpl$Uid;->updateUidProcessStateLocked(I)V
 HSPLcom/android/internal/os/BatteryStatsImpl$Uid;->writeJobCompletionsToParcelLocked(Landroid/os/Parcel;)V
 HSPLcom/android/internal/os/BatteryStatsImpl$UserInfoProvider;->getUserIds()[I
-HSPLcom/android/internal/os/BatteryStatsImpl;-><init>(Lcom/android/internal/os/BatteryStatsImpl$Clocks;Landroid/os/Parcel;)V
+# HSPLcom/android/internal/os/BatteryStatsImpl;-><init>(Lcom/android/internal/os/BatteryStatsImpl$Clocks;Landroid/os/Parcel;)V b/76145463
 HSPLcom/android/internal/os/BatteryStatsImpl;-><init>(Lcom/android/internal/os/BatteryStatsImpl$Clocks;Ljava/io/File;Landroid/os/Handler;Lcom/android/internal/os/BatteryStatsImpl$PlatformIdleStateCallback;Lcom/android/internal/os/BatteryStatsImpl$UserInfoProvider;)V
 HSPLcom/android/internal/os/BatteryStatsImpl;-><init>(Ljava/io/File;Landroid/os/Handler;Lcom/android/internal/os/BatteryStatsImpl$PlatformIdleStateCallback;Lcom/android/internal/os/BatteryStatsImpl$UserInfoProvider;)V
 HSPLcom/android/internal/os/BatteryStatsImpl;->addHistoryBufferLocked(JBLandroid/os/BatteryStats$HistoryItem;)V
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 553acc8..83fab7e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1432,7 +1432,15 @@
         if (mAutoFillResetNeeded) {
             if (!mAutoFillIgnoreFirstResumePause) {
                 View focus = getCurrentFocus();
-                if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
+                // On Activity rotation situation (mRestoredFromBundle is true),
+                // we should not call on AutofillManager in onResume()
+                // since the next Layout pass will do that.
+                // However, there are both cases where Activity#getCurrentFocus()
+                // will return null (window not preserved) and not null (window IS
+                // preserved), so we need to explicitly check for mRestoredFromBundle
+                // here.
+                if (!mRestoredFromBundle && focus != null
+                        && focus.canNotifyAutofillEnterExitEvent()) {
                     // TODO: in Activity killed/recreated case, i.e. SessionLifecycleTest#
                     // testDatasetVisibleWhileAutofilledAppIsLifecycled: the View's initial
                     // window visibility after recreation is INVISIBLE in onResume() and next frame
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 9837deb..28ecb27 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1724,6 +1724,24 @@
     }
 
     @Override
+    public void updateServiceGroup(@NonNull ServiceConnection conn, int group, int importance) {
+        if (conn == null) {
+            throw new IllegalArgumentException("connection is null");
+        }
+        if (mPackageInfo != null) {
+            IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
+                    getOuterContext(), conn);
+            try {
+                ActivityManager.getService().updateServiceGroup(sd, group, importance);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            throw new RuntimeException("Not supported in system context");
+        }
+    }
+
+    @Override
     public void unbindService(ServiceConnection conn) {
         if (conn == null) {
             throw new IllegalArgumentException("connection is null");
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index f27c667..e83bcd0 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -140,6 +140,7 @@
     int bindIsolatedService(in IApplicationThread caller, in IBinder token, in Intent service,
             in String resolvedType, in IServiceConnection connection, int flags,
             in String instanceName, in String callingPackage, int userId);
+    void updateServiceGroup(in IServiceConnection connection, int group, int importance);
     boolean unbindService(in IServiceConnection connection);
     void publishService(in IBinder token, in Intent intent, in IBinder service);
     void setDebugApp(in String packageName, boolean waitForDebugger, boolean persistent);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 450efdf..f2a3e44 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3193,6 +3193,25 @@
     }
 
     /**
+     * Returns the actions that are contextual (marked as SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) out
+     * of the actions in this notification.
+     *
+     * @hide
+     */
+    public List<Notification.Action> getContextualActions() {
+        if (actions == null) return Collections.emptyList();
+
+        List<Notification.Action> contextualActions = new ArrayList<>();
+        for (Notification.Action action : actions) {
+            if (action.getSemanticAction()
+                    == Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) {
+                contextualActions.add(action);
+            }
+        }
+        return contextualActions;
+    }
+
+    /**
      * Builder class for {@link Notification} objects.
      *
      * Provides a convenient way to set the various fields of a {@link Notification} and generate
@@ -7461,7 +7480,11 @@
                 return mRemoteInputHistory;
             }
 
-            private Bundle toBundle() {
+            /**
+             * @hide
+             */
+            @VisibleForTesting
+            public Bundle toBundle() {
                 Bundle bundle = new Bundle();
                 if (mText != null) {
                     bundle.putCharSequence(KEY_TEXT, mText);
diff --git a/core/java/android/app/Person.java b/core/java/android/app/Person.java
index 3884a8d..a2dae3b 100644
--- a/core/java/android/app/Person.java
+++ b/core/java/android/app/Person.java
@@ -22,6 +22,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Objects;
+
 /**
  * Provides an immutable reference to an entity that appears repeatedly on different surfaces of the
  * platform. For example, this could represent the sender of a message.
@@ -121,6 +123,26 @@
     }
 
     @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Person) {
+            final Person other = (Person) obj;
+            return Objects.equals(mName, other.mName)
+                    && mIcon == null ? other.mIcon == null :
+                    (other.mIcon != null && mIcon.sameAs(other.mIcon))
+                    && Objects.equals(mUri, other.mUri)
+                    && Objects.equals(mKey, other.mKey)
+                    && mIsBot == other.mIsBot
+                    && mIsImportant == other.mIsImportant;
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mName, mIcon, mUri, mKey, mIsBot, mIsImportant);
+    }
+
+    @Override
     public int describeContents() {
         return 0;
     }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index f129a71..24ee7f7 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9792,7 +9792,6 @@
         }
     }
 
-
     /**
      * Sets the global Private DNS mode and host to be used.
      * May only be called by the device owner.
@@ -9867,4 +9866,31 @@
             throw re.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Grants the profile owner of the given user access to device identifiers (such as
+     * serial number, IMEI and MEID).
+     *
+     * <p>This lets the profile owner request inclusion of device identifiers when calling
+     * {@link generateKeyPair}.
+     *
+     * <p>This grant is necessary to guarantee that profile owners can access device identifiers.
+     *
+     * <p>Privileged system API - meant to be called by the system, particularly the managed
+     * provisioning app, when a work profile is set up.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void setProfileOwnerCanAccessDeviceIdsForUser(
+            @NonNull ComponentName who, @NonNull UserHandle userHandle) {
+        if (mService == null) {
+            return;
+        }
+        try {
+            mService.grantDeviceIdsAccessToProfileOwner(who, userHandle.getIdentifier());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index ce1f4ef..918c127 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -417,4 +417,6 @@
     void setGlobalPrivateDns(in ComponentName admin, int mode, in String privateDnsHost);
     int getGlobalPrivateDnsMode(in ComponentName admin);
     String getGlobalPrivateDnsHost(in ComponentName admin);
+
+    void grantDeviceIdsAccessToProfileOwner(in ComponentName who, int userId);
 }
diff --git a/core/java/android/app/role/IRoleManager.aidl b/core/java/android/app/role/IRoleManager.aidl
index 2cf13ec2..3ca8ec0 100644
--- a/core/java/android/app/role/IRoleManager.aidl
+++ b/core/java/android/app/role/IRoleManager.aidl
@@ -37,6 +37,8 @@
 
     void clearRoleHoldersAsUser(in String roleName, int userId, in IRoleManagerCallback callback);
 
+    void setRoleNamesFromController(in List<String> roleNames);
+
     boolean addRoleHolderFromController(in String roleName, in String packageName);
 
     boolean removeRoleHolderFromController(in String roleName, in String packageName);
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index ef86b01..7cb245a 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -67,10 +67,33 @@
 
     /**
      * The name of the SMS role.
+     *
+     * @see Intent#CATEGORY_APP_MESSAGING
      */
     public static final String ROLE_SMS = "android.app.role.SMS";
 
     /**
+     * The name of the browser role.
+     *
+     * @see Intent#CATEGORY_APP_BROWSER
+     */
+    public static final String ROLE_BROWSER = "android.app.role.BROWSER";
+
+    /**
+     * The name of the gallery role.
+     *
+     * @see Intent#CATEGORY_APP_GALLERY
+     */
+    public static final String ROLE_GALLERY = "android.app.role.GALLERY";
+
+    /**
+     * The name of the music player role.
+     *
+     * @see Intent#CATEGORY_APP_MUSIC
+     */
+    public static final String ROLE_MUSIC = "android.app.role.MUSIC";
+
+    /**
      * The action used to request user approval of a role for an application.
      *
      * @hide
@@ -92,8 +115,8 @@
      *
      * @hide
      */
-    public static final String PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER =
-            "com.android.permissioncontroller.permission.MANAGE_ROLE_HOLDERS_FROM_CONTROLLER";
+    public static final String PERMISSION_MANAGE_ROLES_FROM_CONTROLLER =
+            "com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER";
 
     @NonNull
     private final Context mContext;
@@ -342,12 +365,36 @@
     }
 
     /**
+     * Set the names of all the available roles. Should only be called from
+     * {@link android.rolecontrollerservice.RoleControllerService}.
+     * <p>
+     * <strong>Note:</strong> Using this API requires holding
+     * {@link #PERMISSION_MANAGE_ROLES_FROM_CONTROLLER}.
+     *
+     * @param roleNames the names of all the available roles
+     *
+     * @throws IllegalArgumentException if the list of role names is {@code null}.
+     *
+     * @hide
+     */
+    @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
+    @SystemApi
+    public void setRoleNamesFromController(@NonNull List<String> roleNames) {
+        Preconditions.checkNotNull(roleNames, "roleNames cannot be null");
+        try {
+            mService.setRoleNamesFromController(roleNames);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Add a specific application to the holders of a role, only modifying records inside
      * {@link RoleManager}. Should only be called from
      * {@link android.rolecontrollerservice.RoleControllerService}.
      * <p>
      * <strong>Note:</strong> Using this API requires holding
-     * {@link #PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER}.
+     * {@link #PERMISSION_MANAGE_ROLES_FROM_CONTROLLER}.
      *
      * @param roleName the name of the role to add the role holder for
      * @param packageName the package name of the application to add to the role holders
@@ -362,7 +409,7 @@
      *
      * @hide
      */
-    @RequiresPermission(PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER)
+    @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
     @SystemApi
     public boolean addRoleHolderFromController(@NonNull String roleName,
             @NonNull String packageName) {
@@ -381,7 +428,7 @@
      * {@link android.rolecontrollerservice.RoleControllerService}.
      * <p>
      * <strong>Note:</strong> Using this API requires holding
-     * {@link #PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER}.
+     * {@link #PERMISSION_MANAGE_ROLES_FROM_CONTROLLER}.
      *
      * @param roleName the name of the role to remove the role holder for
      * @param packageName the package name of the application to remove from the role holders
@@ -396,7 +443,7 @@
      *
      * @hide
      */
-    @RequiresPermission(PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER)
+    @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
     @SystemApi
     public boolean removeRoleHolderFromController(@NonNull String roleName,
             @NonNull String packageName) {
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 0aa0535..235dc5c 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -347,7 +347,7 @@
      * device are requested to be fetched using Service Discovery Protocol
      * <p> Always contains the extra field {@link #EXTRA_DEVICE}
      * <p> Always contains the extra field {@link #EXTRA_UUID}
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} to receive.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_UUID =
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 636b1b9..8d9d340 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -973,11 +973,11 @@
      */
     @UnsupportedAppUsage
     public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
-            int type) {
+            int type, String name) {
         final IBluetoothHeadset service = mService;
         if (service != null && isEnabled()) {
             try {
-                service.phoneStateChanged(numActive, numHeld, callState, number, type);
+                service.phoneStateChanged(numActive, numHeld, callState, number, type, name);
             } catch (RemoteException e) {
                 Log.e(TAG, e.toString());
             }
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index 47c4ee6..6ed7942 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -51,7 +51,7 @@
  */
 public final class BluetoothHearingAid implements BluetoothProfile {
     private static final String TAG = "BluetoothHearingAid";
-    private static final boolean DBG = false;
+    private static final boolean DBG = true;
     private static final boolean VDBG = false;
 
     /**
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 004417b..cec8ef5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -425,6 +425,15 @@
      */
     public static final int BIND_EXTERNAL_SERVICE = 0x80000000;
 
+    /**
+     * These bind flags reduce the strength of the binding such that we shouldn't
+     * consider it as pulling the process up to the level of the one that is bound to it.
+     * @hide
+     */
+    public static final int BIND_REDUCTION_FLAGS =
+            Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_WAIVE_PRIORITY
+                    | Context.BIND_ADJUST_BELOW_PERCEPTIBLE | Context.BIND_NOT_VISIBLE;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "RECEIVER_VISIBLE_" }, value = {
             RECEIVER_VISIBLE_TO_INSTANT_APPS
@@ -2982,6 +2991,31 @@
     }
 
     /**
+     * For a service previously bound with {@link #bindService} or a related method, change
+     * how the system manages that service's process in relation to other processes.  This
+     * doesn't modify the original bind flags that were passed in when binding, but adjusts
+     * how the process will be managed in some cases based on those flags.  Currently only
+     * works on isolated processes (will be ignored for non-isolated processes).
+     *
+     * @param conn The connection interface previously supplied to bindService().  This
+     *             parameter must not be null.
+     * @param group A group to put this connection's process in.  Upon calling here, this
+     *              will override any previous group that was set for that process.  The group
+     *              tells the system about processes that are logically grouped together, so
+     *              should be managed as one unit of importance (such as when being considered
+     *              a recently used app).  All processes in the same app with the same group
+     *              are considered to be related.  Supplying 0 reverts to the default behavior
+     *              of not grouping.
+     * @param importance Additional importance of the processes within a group.  Upon calling
+     *                   here, this will override any previous group that was set for that
+     *                   process.  This fine-tunes process killing of all processes within
+     *                   a related groups -- higher importance values will be killed before
+     *                   lower ones.
+     */
+    public abstract void updateServiceGroup(@NonNull ServiceConnection conn, int group,
+            int importance);
+
+    /**
      * Disconnect from an application service.  You will no longer receive
      * calls as the service is restarted, and the service is now allowed to
      * stop at any time.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 88696b0e..2db44b4 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -726,6 +726,11 @@
     }
 
     @Override
+    public void updateServiceGroup(ServiceConnection conn, int group, int importance) {
+        mBase.updateServiceGroup(conn, group, importance);
+    }
+
+    @Override
     public void unbindService(ServiceConnection conn) {
         mBase.unbindService(conn);
     }
diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl
index 287a5ed..8160338 100644
--- a/core/java/android/os/IThermalService.aidl
+++ b/core/java/android/os/IThermalService.aidl
@@ -17,6 +17,7 @@
 package android.os;
 
 import android.os.IThermalEventListener;
+import android.os.IThermalStatusListener;
 import android.os.Temperature;
 
 import java.util.List;
@@ -30,31 +31,60 @@
       * @param listener the IThermalEventListener to be notified.
       * {@hide}
       */
-    void registerThermalEventListener(in IThermalEventListener listener);
+    boolean registerThermalEventListener(in IThermalEventListener listener);
+
     /**
       * Register a listener for thermal events on given temperature type.
       * @param listener the IThermalEventListener to be notified.
       * @param type the temperature type IThermalEventListener to be notified.
+      * @return true if registered successfully.
       * {@hide}
       */
-    void registerThermalEventListenerWithType(in IThermalEventListener listener, in int type);
+    boolean registerThermalEventListenerWithType(in IThermalEventListener listener, in int type);
+
     /**
       * Unregister a previously-registered listener for thermal events.
       * @param listener the IThermalEventListener to no longer be notified.
+      * @return true if unregistered successfully.
       * {@hide}
       */
-    void unregisterThermalEventListener(in IThermalEventListener listener);
+    boolean unregisterThermalEventListener(in IThermalEventListener listener);
+
     /**
       * Get current temperature with its throttling status.
       * @return list of android.os.Temperature
       * {@hide}
       */
     List<Temperature> getCurrentTemperatures();
+
     /**
       * Get current temperature with its throttling status on given temperature type.
       * @param type the temperature type to query.
-      * @return list of android.os.Temperature
+      * @return list of {@link android.os.Temperature}.
       * {@hide}
       */
     List<Temperature> getCurrentTemperaturesWithType(in int type);
+
+    /**
+      * Register a listener for thermal status change.
+      * @param listener the IThermalStatusListener to be notified.
+      * @return true if registered successfully.
+      * {@hide}
+      */
+    boolean registerThermalStatusListener(in IThermalStatusListener listener);
+
+    /**
+      * Unregister a previously-registered listener for thermal status.
+      * @param listener the IThermalStatusListener to no longer be notified.
+      * @return true if unregistered successfully.
+      * {@hide}
+      */
+    boolean unregisterThermalStatusListener(in IThermalStatusListener listener);
+
+    /**
+      * Get current thermal status.
+      * @return status defined in {@link android.os.Temperature}.
+      * {@hide}
+      */
+    int getCurrentStatus();
 }
diff --git a/core/java/android/os/IThermalStatusListener.aidl b/core/java/android/os/IThermalStatusListener.aidl
new file mode 100644
index 0000000..a6da7d0
--- /dev/null
+++ b/core/java/android/os/IThermalStatusListener.aidl
@@ -0,0 +1,29 @@
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+/**
+ * Listener for thermal status.
+ * {@hide}
+ */
+oneway interface IThermalStatusListener {
+    /**
+     * Called when overall thermal throttling status changed.
+     * @param status defined in {@link android.os.Temperature#ThrottlingStatus}.
+     */
+    void onStatusChange(int status);
+}
diff --git a/core/java/android/os/Temperature.java b/core/java/android/os/Temperature.java
index 37ed52c..bf85fbd 100644
--- a/core/java/android/os/Temperature.java
+++ b/core/java/android/os/Temperature.java
@@ -25,9 +25,7 @@
 
 /**
  * Temperature values used by IThermalService.
- */
-
-/**
+ *
  * @hide
  */
 public class Temperature implements Parcelable {
@@ -40,7 +38,6 @@
     /** The level of the sensor is currently in throttling */
     private int mStatus;
 
-    /** @hide */
     @IntDef(prefix = { "THROTTLING_" }, value = {
             THROTTLING_NONE,
             THROTTLING_LIGHT,
@@ -62,7 +59,6 @@
     public static final int THROTTLING_WARNING = ThrottlingSeverity.WARNING;
     public static final int THROTTLING_SHUTDOWN = ThrottlingSeverity.SHUTDOWN;
 
-    /** @hide */
     @IntDef(prefix = { "TYPE_" }, value = {
             TYPE_UNKNOWN,
             TYPE_CPU,
@@ -95,19 +91,28 @@
      *
      * @return true if a temperature type is valid otherwise false.
      */
-    public static boolean isValidType(int type) {
+    public static boolean isValidType(@Type int type) {
         return type >= TYPE_UNKNOWN && type <= TYPE_BCL_PERCENTAGE;
     }
 
+    /**
+     * Verify a valid throttling status.
+     *
+     * @return true if a status is valid otherwise false.
+     */
+    public static boolean isValidStatus(@ThrottlingStatus int status) {
+        return status >= THROTTLING_NONE && status <= THROTTLING_SHUTDOWN;
+    }
+
     public Temperature() {
         this(Float.NaN, TYPE_UNKNOWN, "", THROTTLING_NONE);
     }
 
-    public Temperature(float value, @Type int type, String name, int status) {
+    public Temperature(float value, @Type int type, String name, @ThrottlingStatus int status) {
         mValue = value;
         mType = isValidType(type) ? type : TYPE_UNKNOWN;
         mName = name;
-        mStatus = status;
+        mStatus = isValidStatus(status) ? status : THROTTLING_NONE;
     }
 
     /**
diff --git a/core/java/android/preference/CheckBoxPreference.java b/core/java/android/preference/CheckBoxPreference.java
index fee3f0f1..948c6aa 100644
--- a/core/java/android/preference/CheckBoxPreference.java
+++ b/core/java/android/preference/CheckBoxPreference.java
@@ -27,11 +27,18 @@
  * functionality.
  * <p>
  * This preference will store a boolean into the SharedPreferences.
- * 
+ *
  * @attr ref android.R.styleable#CheckBoxPreference_summaryOff
  * @attr ref android.R.styleable#CheckBoxPreference_summaryOn
  * @attr ref android.R.styleable#CheckBoxPreference_disableDependentsState
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public class CheckBoxPreference extends TwoStatePreference {
 
     public CheckBoxPreference(Context context, AttributeSet attrs, int defStyleAttr) {
diff --git a/core/java/android/preference/DialogPreference.java b/core/java/android/preference/DialogPreference.java
index 4b5a7b4..96c8589 100644
--- a/core/java/android/preference/DialogPreference.java
+++ b/core/java/android/preference/DialogPreference.java
@@ -50,7 +50,14 @@
  * @attr ref android.R.styleable#DialogPreference_dialogLayout
  * @attr ref android.R.styleable#DialogPreference_positiveButtonText
  * @attr ref android.R.styleable#DialogPreference_negativeButtonText
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public abstract class DialogPreference extends Preference implements
         DialogInterface.OnClickListener, DialogInterface.OnDismissListener,
         PreferenceManager.OnActivityDestroyListener {
diff --git a/core/java/android/preference/EditTextPreference.java b/core/java/android/preference/EditTextPreference.java
index 4d2ac67..c09cec8 100644
--- a/core/java/android/preference/EditTextPreference.java
+++ b/core/java/android/preference/EditTextPreference.java
@@ -42,7 +42,14 @@
  * This preference will store a string into the SharedPreferences.
  * <p>
  * See {@link android.R.styleable#EditText EditText Attributes}.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public class EditTextPreference extends DialogPreference {
     /**
      * The edit text shown in the dialog.
diff --git a/core/java/android/preference/GenericInflater.java b/core/java/android/preference/GenericInflater.java
index 3319e64..7edc987 100644
--- a/core/java/android/preference/GenericInflater.java
+++ b/core/java/android/preference/GenericInflater.java
@@ -16,13 +16,6 @@
 
 package android.preference;
 
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.util.HashMap;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 import android.annotation.XmlRes;
 import android.content.Context;
 import android.content.res.XmlResourceParser;
@@ -32,6 +25,13 @@
 import android.view.InflateException;
 import android.view.LayoutInflater;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+
 // TODO: fix generics
 /**
  * Generic XML inflater. This has been adapted from {@link LayoutInflater} and
@@ -41,7 +41,14 @@
  * @param T The type of the items to inflate
  * @param P The type of parents (that is those items that contain other items).
  *            Must implement {@link GenericInflater.Parent}
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 abstract class GenericInflater<T, P extends GenericInflater.Parent> {
     private final boolean DEBUG = false;
 
diff --git a/core/java/android/preference/ListPreference.java b/core/java/android/preference/ListPreference.java
index c0c71af..14c1dc81 100644
--- a/core/java/android/preference/ListPreference.java
+++ b/core/java/android/preference/ListPreference.java
@@ -36,7 +36,14 @@
  *
  * @attr ref android.R.styleable#ListPreference_entries
  * @attr ref android.R.styleable#ListPreference_entryValues
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public class ListPreference extends DialogPreference {
     private CharSequence[] mEntries;
     private CharSequence[] mEntryValues;
diff --git a/core/java/android/preference/MultiCheckPreference.java b/core/java/android/preference/MultiCheckPreference.java
index c1260a4..e3d0e26 100644
--- a/core/java/android/preference/MultiCheckPreference.java
+++ b/core/java/android/preference/MultiCheckPreference.java
@@ -16,8 +16,6 @@
 
 package android.preference;
 
-import java.util.Arrays;
-
 import android.annotation.ArrayRes;
 import android.app.AlertDialog.Builder;
 import android.content.Context;
@@ -27,6 +25,8 @@
 import android.os.Parcelable;
 import android.util.AttributeSet;
 
+import java.util.Arrays;
+
 /**
  * @hide
  * A {@link Preference} that displays a list of entries as
@@ -34,7 +34,14 @@
  * 
  * @attr ref android.R.styleable#ListPreference_entries
  * @attr ref android.R.styleable#ListPreference_entryValues
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public class MultiCheckPreference extends DialogPreference {
     private CharSequence[] mEntries;
     private String[] mEntryValues;
diff --git a/core/java/android/preference/MultiSelectListPreference.java b/core/java/android/preference/MultiSelectListPreference.java
index 138bd878..43182d9 100644
--- a/core/java/android/preference/MultiSelectListPreference.java
+++ b/core/java/android/preference/MultiSelectListPreference.java
@@ -35,10 +35,17 @@
  * This preference will store a set of strings into the SharedPreferences.
  * This set will contain one or more values from the
  * {@link #setEntryValues(CharSequence[])} array.
- * 
+ *
  * @attr ref android.R.styleable#MultiSelectListPreference_entries
  * @attr ref android.R.styleable#MultiSelectListPreference_entryValues
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public class MultiSelectListPreference extends DialogPreference {
     private CharSequence[] mEntries;
     private CharSequence[] mEntryValues;
@@ -65,25 +72,25 @@
     public MultiSelectListPreference(Context context, AttributeSet attrs) {
         this(context, attrs, com.android.internal.R.attr.dialogPreferenceStyle);
     }
-    
+
     public MultiSelectListPreference(Context context) {
         this(context, null);
     }
-    
+
     /**
      * Sets the human-readable entries to be shown in the list. This will be
      * shown in subsequent dialogs.
      * <p>
      * Each entry must have a corresponding index in
      * {@link #setEntryValues(CharSequence[])}.
-     * 
+     *
      * @param entries The entries.
      * @see #setEntryValues(CharSequence[])
      */
     public void setEntries(CharSequence[] entries) {
         mEntries = entries;
     }
-    
+
     /**
      * @see #setEntries(CharSequence[])
      * @param entriesResId The entries array as a resource.
@@ -91,21 +98,21 @@
     public void setEntries(@ArrayRes int entriesResId) {
         setEntries(getContext().getResources().getTextArray(entriesResId));
     }
-    
+
     /**
      * The list of entries to be shown in the list in subsequent dialogs.
-     * 
+     *
      * @return The list as an array.
      */
     public CharSequence[] getEntries() {
         return mEntries;
     }
-    
+
     /**
      * The array to find the value to save for a preference when an entry from
      * entries is selected. If a user clicks on the second item in entries, the
      * second item in this array will be saved to the preference.
-     * 
+     *
      * @param entryValues The array to be used as values to save for the preference.
      */
     public void setEntryValues(CharSequence[] entryValues) {
@@ -119,20 +126,20 @@
     public void setEntryValues(@ArrayRes int entryValuesResId) {
         setEntryValues(getContext().getResources().getTextArray(entryValuesResId));
     }
-    
+
     /**
      * Returns the array of values to be saved for the preference.
-     * 
+     *
      * @return The array of values.
      */
     public CharSequence[] getEntryValues() {
         return mEntryValues;
     }
-    
+
     /**
      * Sets the value of the key. This should contain entries in
      * {@link #getEntryValues()}.
-     * 
+     *
      * @param values The values to set for the key.
      */
     public void setValues(Set<String> values) {
@@ -141,17 +148,17 @@
 
         persistStringSet(values);
     }
-    
+
     /**
      * Retrieves the current value of the key.
      */
     public Set<String> getValues() {
         return mValues;
     }
-    
+
     /**
      * Returns the index of the given value (in the entry values array).
-     * 
+     *
      * @param value The value whose index should be returned.
      * @return The index of the value, or -1 if not found.
      */
@@ -165,17 +172,17 @@
         }
         return -1;
     }
-    
+
     @Override
     protected void onPrepareDialogBuilder(Builder builder) {
         super.onPrepareDialogBuilder(builder);
-        
+
         if (mEntries == null || mEntryValues == null) {
             throw new IllegalStateException(
                     "MultiSelectListPreference requires an entries array and " +
                     "an entryValues array.");
         }
-        
+
         boolean[] checkedItems = getSelectedItems();
         builder.setMultiChoiceItems(mEntries, checkedItems,
                 new DialogInterface.OnMultiChoiceClickListener() {
@@ -190,24 +197,24 @@
         mNewValues.clear();
         mNewValues.addAll(mValues);
     }
-    
+
     private boolean[] getSelectedItems() {
         final CharSequence[] entries = mEntryValues;
         final int entryCount = entries.length;
         final Set<String> values = mValues;
         boolean[] result = new boolean[entryCount];
-        
+
         for (int i = 0; i < entryCount; i++) {
             result[i] = values.contains(entries[i].toString());
         }
-        
+
         return result;
     }
-    
+
     @Override
     protected void onDialogClosed(boolean positiveResult) {
         super.onDialogClosed(positiveResult);
-        
+
         if (positiveResult && mPreferenceChanged) {
             final Set<String> values = mNewValues;
             if (callChangeListener(values)) {
@@ -216,25 +223,25 @@
         }
         mPreferenceChanged = false;
     }
-    
+
     @Override
     protected Object onGetDefaultValue(TypedArray a, int index) {
         final CharSequence[] defaultValues = a.getTextArray(index);
         final int valueCount = defaultValues.length;
         final Set<String> result = new HashSet<String>();
-        
+
         for (int i = 0; i < valueCount; i++) {
             result.add(defaultValues[i].toString());
         }
-        
+
         return result;
     }
-    
+
     @Override
     protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
         setValues(restoreValue ? getPersistedStringSet(mValues) : (Set<String>) defaultValue);
     }
-    
+
     @Override
     protected Parcelable onSaveInstanceState() {
         final Parcelable superState = super.onSaveInstanceState();
@@ -242,42 +249,42 @@
             // No need to save instance state
             return superState;
         }
-        
+
         final SavedState myState = new SavedState(superState);
         myState.values = getValues();
         return myState;
     }
-    
+
     private static class SavedState extends BaseSavedState {
         Set<String> values;
-        
+
         public SavedState(Parcel source) {
             super(source);
             values = new HashSet<String>();
             String[] strings = source.readStringArray();
-            
+
             final int stringCount = strings.length;
             for (int i = 0; i < stringCount; i++) {
                 values.add(strings[i]);
             }
         }
-        
+
         public SavedState(Parcelable superState) {
             super(superState);
         }
-        
+
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             super.writeToParcel(dest, flags);
             dest.writeStringArray(values.toArray(new String[0]));
         }
-        
+
         public static final Parcelable.Creator<SavedState> CREATOR =
                 new Parcelable.Creator<SavedState>() {
             public SavedState createFromParcel(Parcel in) {
                 return new SavedState(in);
             }
-            
+
             public SavedState[] newArray(int size) {
                 return new SavedState[size];
             }
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 2387657..3c1ba9d 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -86,7 +86,14 @@
  * @attr ref android.R.styleable#Preference_recycleEnabled
  * @attr ref android.R.styleable#Preference_singleLineTitle
  * @attr ref android.R.styleable#Preference_iconSpaceReserved
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public class Preference implements Comparable<Preference> {
     /**
      * Specify for {@link #setOrder(int)} if a specific order is not required.
@@ -165,7 +172,14 @@
      * {@link Preference} has been changed by the user and is
      * about to be set and/or persisted.  This gives the client a chance
      * to prevent setting and/or persisting the value.
+     *
+     * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+     *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+     *      Preference Library</a> for consistent behavior across all devices.
+     *      For more information on using the AndroidX Preference Library see
+     *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
      */
+    @Deprecated
     public interface OnPreferenceChangeListener {
         /**
          * Called when a Preference has been changed by the user. This is
@@ -182,7 +196,14 @@
     /**
      * Interface definition for a callback to be invoked when a {@link Preference} is
      * clicked.
+     *
+     * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+     *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+     *      Preference Library</a> for consistent behavior across all devices.
+     *      For more information on using the AndroidX Preference Library see
+     *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
      */
+    @Deprecated
     public interface OnPreferenceClickListener {
         /**
          * Called when a Preference has been clicked.
@@ -2070,7 +2091,14 @@
 
     /**
      * A base class for managing the instance state of a {@link Preference}.
+     *
+     * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+     *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+     *      Preference Library</a> for consistent behavior across all devices.
+     *      For more information on using the AndroidX Preference Library see
+     *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
      */
+    @Deprecated
     public static class BaseSavedState extends AbsSavedState {
         public BaseSavedState(Parcel source) {
             super(source);
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 8ed2605..eab5937 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -97,34 +97,13 @@
  * guide.</p>
  * </div>
  *
- * <a name="SampleCode"></a>
- * <h3>Sample Code</h3>
- *
- * <p>The following sample code shows a simple preference activity that
- * has two different sets of preferences.  The implementation, consisting
- * of the activity itself as well as its two preference fragments is:</p>
- *
- * {@sample development/samples/ApiDemos/src/com/example/android/apis/preference/PreferenceWithHeaders.java
- *      activity}
- *
- * <p>The preference_headers resource describes the headers to be displayed
- * and the fragments associated with them.  It is:
- *
- * {@sample development/samples/ApiDemos/res/xml/preference_headers.xml headers}
- *
- * <p>The first header is shown by Prefs1Fragment, which populates itself
- * from the following XML resource:</p>
- *
- * {@sample development/samples/ApiDemos/res/xml/fragmented_preferences.xml preferences}
- *
- * <p>Note that this XML resource contains a preference screen holding another
- * fragment, the Prefs1FragmentInner implemented here.  This allows the user
- * to traverse down a hierarchy of preferences; pressing back will pop each
- * fragment off the stack to return to the previous preferences.
- *
- * <p>See {@link PreferenceFragment} for information on implementing the
- * fragments themselves.
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public abstract class PreferenceActivity extends ListActivity implements
         PreferenceManager.OnPreferenceTreeClickListener,
         PreferenceFragment.OnPreferenceStartFragmentCallback {
@@ -337,7 +316,14 @@
 
     /**
      * Description of a single Header item that the user can select.
+     *
+     * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+     *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+     *      Preference Library</a> for consistent behavior across all devices.
+     *      For more information on using the AndroidX Preference Library see
+     *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
      */
+    @Deprecated
     public static final class Header implements Parcelable {
         /**
          * Identifier for this header, to correlate with a new list when
diff --git a/core/java/android/preference/PreferenceCategory.java b/core/java/android/preference/PreferenceCategory.java
index 253481b..887e468 100644
--- a/core/java/android/preference/PreferenceCategory.java
+++ b/core/java/android/preference/PreferenceCategory.java
@@ -29,7 +29,14 @@
  * read the <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>
  * guide.</p>
  * </div>
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public class PreferenceCategory extends PreferenceGroup {
     private static final String TAG = "PreferenceCategory";
 
diff --git a/core/java/android/preference/PreferenceDataStore.java b/core/java/android/preference/PreferenceDataStore.java
index 8caa404..5171632 100644
--- a/core/java/android/preference/PreferenceDataStore.java
+++ b/core/java/android/preference/PreferenceDataStore.java
@@ -39,7 +39,14 @@
  *
  * @see Preference#setPreferenceDataStore(PreferenceDataStore)
  * @see PreferenceManager#setPreferenceDataStore(PreferenceDataStore)
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public interface PreferenceDataStore {
 
     /**
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index 548895e..d6c069f0 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -88,25 +88,14 @@
  * guide.</p>
  * </div>
  *
- * <a name="SampleCode"></a>
- * <h3>Sample Code</h3>
- *
- * <p>The following sample code shows a simple preference fragment that is
- * populated from a resource.  The resource it loads is:</p>
- *
- * {@sample development/samples/ApiDemos/res/xml/preferences.xml preferences}
- *
- * <p>The fragment implementation itself simply populates the preferences
- * when created.  Note that the preferences framework takes care of loading
- * the current values out of the app preferences and writing them when changed:</p>
- *
- * {@sample development/samples/ApiDemos/src/com/example/android/apis/preference/FragmentPreferences.java
- *      fragment}
- *
  * @see Preference
  * @see PreferenceScreen
  *
- * @deprecated Use {@link android.support.v7.preference.PreferenceFragmentCompat}
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
 @Deprecated
 public abstract class PreferenceFragment extends Fragment implements
diff --git a/core/java/android/preference/PreferenceFrameLayout.java b/core/java/android/preference/PreferenceFrameLayout.java
index 886338f..c667824 100644
--- a/core/java/android/preference/PreferenceFrameLayout.java
+++ b/core/java/android/preference/PreferenceFrameLayout.java
@@ -24,7 +24,14 @@
 
 /**
  * @hide
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public class PreferenceFrameLayout extends FrameLayout {
     private static final int DEFAULT_BORDER_TOP = 0;
     private static final int DEFAULT_BORDER_BOTTOM = 0;
diff --git a/core/java/android/preference/PreferenceGroup.java b/core/java/android/preference/PreferenceGroup.java
index f135b26..b33ea4e 100644
--- a/core/java/android/preference/PreferenceGroup.java
+++ b/core/java/android/preference/PreferenceGroup.java
@@ -16,15 +16,16 @@
 
 package android.preference;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
 /**
  * A container for multiple
  * {@link Preference} objects. It is a base class for  Preference objects that are
@@ -38,7 +39,14 @@
  * </div>
  *
  * @attr ref android.R.styleable#PreferenceGroup_orderingFromXml
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public abstract class PreferenceGroup extends Preference implements GenericInflater.Parent<Preference> {
     /**
      * The container for child {@link Preference}s. This is sorted based on the
diff --git a/core/java/android/preference/PreferenceGroupAdapter.java b/core/java/android/preference/PreferenceGroupAdapter.java
index bee45ab..fb41ea8 100644
--- a/core/java/android/preference/PreferenceGroupAdapter.java
+++ b/core/java/android/preference/PreferenceGroupAdapter.java
@@ -16,10 +16,6 @@
 
 package android.preference;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.preference.Preference.OnPreferenceChangeInternalListener;
@@ -30,6 +26,10 @@
 import android.widget.FrameLayout;
 import android.widget.ListView;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
 /**
  * An adapter that returns the {@link Preference} contained in this group.
  * In most cases, this adapter should be the base class for any custom
@@ -49,7 +49,14 @@
  * @see PreferenceCategoryAdapter
  *
  * @hide
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public class PreferenceGroupAdapter extends BaseAdapter
         implements OnPreferenceChangeInternalListener {
     
diff --git a/core/java/android/preference/PreferenceInflater.java b/core/java/android/preference/PreferenceInflater.java
index 727fbca..04ad107 100644
--- a/core/java/android/preference/PreferenceInflater.java
+++ b/core/java/android/preference/PreferenceInflater.java
@@ -16,16 +16,16 @@
 
 package android.preference;
 
-import com.android.internal.util.XmlUtils;
+import android.content.Context;
+import android.content.Intent;
+import android.util.AttributeSet;
 
-import java.io.IOException;
+import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
-import android.content.Context;
-import android.content.Intent;
-import android.util.AttributeSet;
+import java.io.IOException;
 
 /**
  * The {@link PreferenceInflater} is used to inflate preference hierarchies from
@@ -34,7 +34,14 @@
  * Do not construct this directly, instead use
  * {@link Context#getSystemService(String)} with
  * {@link Context#PREFERENCE_INFLATER_SERVICE}.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 class PreferenceInflater extends GenericInflater<Preference, PreferenceGroup> {
     private static final String TAG = "PreferenceInflater";
     private static final String INTENT_TAG_NAME = "intent";
diff --git a/core/java/android/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java
index dfee1af..f741bd6 100644
--- a/core/java/android/preference/PreferenceManager.java
+++ b/core/java/android/preference/PreferenceManager.java
@@ -47,7 +47,14 @@
  * {@link PreferenceActivity#addPreferencesFromResource(int)}.
  *
  * @see PreferenceActivity
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public class PreferenceManager {
 
     private static final String TAG = "PreferenceManager";
@@ -1004,7 +1011,14 @@
      * clicked.
      *
      * @hide
+     *
+     * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+     *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+     *      Preference Library</a> for consistent behavior across all devices.
+     *      For more information on using the AndroidX Preference Library see
+     *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
      */
+    @Deprecated
     public interface OnPreferenceTreeClickListener {
         /**
          * Called when a preference in the tree rooted at this
@@ -1021,7 +1035,14 @@
     /**
      * Interface definition for a class that will be called when the container's activity
      * receives an activity result.
+     *
+     * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+     *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+     *      Preference Library</a> for consistent behavior across all devices.
+     *      For more information on using the AndroidX Preference Library see
+     *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
      */
+    @Deprecated
     public interface OnActivityResultListener {
 
         /**
@@ -1036,7 +1057,14 @@
     /**
      * Interface definition for a class that will be called when the container's activity
      * is stopped.
+     *
+     * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+     *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+     *      Preference Library</a> for consistent behavior across all devices.
+     *      For more information on using the AndroidX Preference Library see
+     *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
      */
+    @Deprecated
     public interface OnActivityStopListener {
 
         /**
@@ -1048,7 +1076,14 @@
     /**
      * Interface definition for a class that will be called when the container's activity
      * is destroyed.
+     *
+     * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+     *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+     *      Preference Library</a> for consistent behavior across all devices.
+     *      For more information on using the AndroidX Preference Library see
+     *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
      */
+    @Deprecated
     public interface OnActivityDestroyListener {
 
         /**
diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java
index dd00a53..c7653c8 100644
--- a/core/java/android/preference/PreferenceScreen.java
+++ b/core/java/android/preference/PreferenceScreen.java
@@ -86,7 +86,14 @@
  * </div>
  *
  * @see PreferenceCategory
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public final class PreferenceScreen extends PreferenceGroup implements AdapterView.OnItemClickListener,
         DialogInterface.OnDismissListener {
 
diff --git a/core/java/android/preference/RingtonePreference.java b/core/java/android/preference/RingtonePreference.java
index cd751cd..025aad0 100644
--- a/core/java/android/preference/RingtonePreference.java
+++ b/core/java/android/preference/RingtonePreference.java
@@ -40,7 +40,14 @@
  * @attr ref android.R.styleable#RingtonePreference_ringtoneType
  * @attr ref android.R.styleable#RingtonePreference_showDefault
  * @attr ref android.R.styleable#RingtonePreference_showSilent
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public class RingtonePreference extends Preference implements
         PreferenceManager.OnActivityResultListener {
 
diff --git a/core/java/android/preference/SeekBarDialogPreference.java b/core/java/android/preference/SeekBarDialogPreference.java
index a8e5992..32ef821 100644
--- a/core/java/android/preference/SeekBarDialogPreference.java
+++ b/core/java/android/preference/SeekBarDialogPreference.java
@@ -28,7 +28,14 @@
 
 /**
  * @hide
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public class SeekBarDialogPreference extends DialogPreference {
     private final Drawable mMyIcon;
 
diff --git a/core/java/android/preference/SeekBarPreference.java b/core/java/android/preference/SeekBarPreference.java
index cd35f3d..f789e31 100644
--- a/core/java/android/preference/SeekBarPreference.java
+++ b/core/java/android/preference/SeekBarPreference.java
@@ -29,7 +29,14 @@
 
 /**
  * @hide
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public class SeekBarPreference extends Preference
         implements OnSeekBarChangeListener {
 
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index a871425..f01d5b1 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -45,7 +45,14 @@
 /**
  * Turns a {@link SeekBar} into a volume control.
  * @hide
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callback {
     private static final String TAG = "SeekBarVolumizer";
 
diff --git a/core/java/android/preference/SwitchPreference.java b/core/java/android/preference/SwitchPreference.java
index 1ec18bb..9dea1c8 100644
--- a/core/java/android/preference/SwitchPreference.java
+++ b/core/java/android/preference/SwitchPreference.java
@@ -36,7 +36,14 @@
  * @attr ref android.R.styleable#SwitchPreference_switchTextOff
  * @attr ref android.R.styleable#SwitchPreference_switchTextOn
  * @attr ref android.R.styleable#SwitchPreference_disableDependentsState
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public class SwitchPreference extends TwoStatePreference {
     @UnsupportedAppUsage
     private final Listener mListener = new Listener();
diff --git a/core/java/android/preference/TwoStatePreference.java b/core/java/android/preference/TwoStatePreference.java
index 2079a63..454472a 100644
--- a/core/java/android/preference/TwoStatePreference.java
+++ b/core/java/android/preference/TwoStatePreference.java
@@ -32,7 +32,14 @@
  * Common base class for preferences that have two selectable states, persist a
  * boolean value in SharedPreferences, and may have dependent preferences that are
  * enabled/disabled based on the current state.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public abstract class TwoStatePreference extends Preference {
 
     private CharSequence mSummaryOn;
diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java
index ea1d1eb..92d848a 100644
--- a/core/java/android/preference/VolumePreference.java
+++ b/core/java/android/preference/VolumePreference.java
@@ -31,7 +31,14 @@
 
 /**
  * @hide
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ *      <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ *      Preference Library</a> for consistent behavior across all devices. For more information on
+ *      using the AndroidX Preference Library see
+ *      <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
  */
+@Deprecated
 public class VolumePreference extends SeekBarDialogPreference implements
         PreferenceManager.OnActivityStopListener, View.OnKeyListener, SeekBarVolumizer.Callback {
     @UnsupportedAppUsage
diff --git a/core/java/android/preference/package.html b/core/java/android/preference/package.html
index d24d5bb..382ed6e 100644
--- a/core/java/android/preference/package.html
+++ b/core/java/android/preference/package.html
@@ -1,23 +1,9 @@
 <HTML>
 <BODY>
-Provides classes that manage application preferences and implement the preferences UI.
-Using these ensures that all the preferences within each application are maintained 
-in the same manner and the user experience is consistent with that of the system and 
-other applications.
-<p>
-The preferences portion of an application 
-should be ran as a separate {@link android.app.Activity} that extends
-the {@link android.preference.PreferenceActivity} class. In the PreferenceActivity, a 
-{@link android.preference.PreferenceScreen} object should be the root element of the layout. 
-The PreferenceScreen contains {@link android.preference.Preference} elements such as a 
-{@link android.preference.CheckBoxPreference}, {@link android.preference.EditTextPreference}, 
-{@link android.preference.ListPreference}, {@link android.preference.PreferenceCategory},
-or {@link android.preference.RingtonePreference}. </p>
-<p>
-All settings made for a given {@link android.preference.Preference} will be automatically saved
-to the application's instance of {@link android.content.SharedPreferences}. Access to the 
-SharedPreferences is simple with {@link android.preference.Preference#getSharedPreferences()}.</p>
-<p>
-Note that saved preferences are accessible only to the application that created them.</p>
+These classes are deprecated. Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+<a href="{@docRoot}reference/androidx/preference/package-summary.html">
+Preference Library</a> for consistent behavior across all devices. For more information on
+using the AndroidX Preference Library see
+<a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
 </BODY>
 </HTML>
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b266648..30bcbf4 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1692,6 +1692,15 @@
     /** @hide - Private call() method to write to 'configuration' table */
     public static final String CALL_METHOD_PUT_CONFIG = "PUT_config";
 
+    /** @hide - Private call() method to delete from the 'system' table */
+    public static final String CALL_METHOD_DELETE_SYSTEM = "DELETE_system";
+
+    /** @hide - Private call() method to delete from the 'secure' table */
+    public static final String CALL_METHOD_DELETE_SECURE = "DELETE_secure";
+
+    /** @hide - Private call() method to delete from the 'global' table */
+    public static final String CALL_METHOD_DELETE_GLOBAL = "DELETE_global";
+
     /** @hide - Private call() method to reset to defaults the 'global' table */
     public static final String CALL_METHOD_RESET_GLOBAL = "RESET_global";
 
diff --git a/core/java/android/service/carrier/CarrierIdentifier.java b/core/java/android/service/carrier/CarrierIdentifier.java
index 09bba4b..e930f40 100644
--- a/core/java/android/service/carrier/CarrierIdentifier.java
+++ b/core/java/android/service/carrier/CarrierIdentifier.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.TelephonyManager;
 
 import com.android.internal.telephony.uicc.IccUtils;
 
@@ -26,7 +27,10 @@
 
 /**
  * Used to pass info to CarrierConfigService implementations so they can decide what values to
- * return.
+ * return. Instead of passing mcc, mnc, gid1, gid2, spn, imsi to locate carrier information,
+ * CarrierIdentifier also include carrier id {@link TelephonyManager#getSimCarrierId()},
+ * a platform-wide unique identifier for each carrier. CarrierConfigService can directly use
+ * carrier id as the key to look up the carrier info.
  */
 public class CarrierIdentifier implements Parcelable {
 
@@ -49,15 +53,40 @@
     private @Nullable String mImsi;
     private @Nullable String mGid1;
     private @Nullable String mGid2;
+    private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+    private int mPreciseCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
 
     public CarrierIdentifier(String mcc, String mnc, @Nullable String spn, @Nullable String imsi,
             @Nullable String gid1, @Nullable String gid2) {
+        this(mcc, mnc, spn, imsi, gid1, gid2, TelephonyManager.UNKNOWN_CARRIER_ID,
+                TelephonyManager.UNKNOWN_CARRIER_ID);
+    }
+
+    /**
+     * @param mcc mobile country code
+     * @param mnc mobile network code
+     * @param spn service provider name
+     * @param imsi International Mobile Subscriber Identity {@link TelephonyManager#getSubscriberId()}
+     * @param gid1 group id level 1 {@link TelephonyManager#getGroupIdLevel1()}
+     * @param gid2 group id level 2
+     * @param carrierid carrier unique identifier {@link TelephonyManager#getSimCarrierId()}, used
+     *                  to uniquely identify the carrier and look up the carrier configurations.
+     * @param preciseCarrierId precise carrier identifier {@link TelephonyManager#getSimPreciseCarrierId()}
+     * @hide
+     *
+     * TODO: expose this to public API
+     */
+    public CarrierIdentifier(String mcc, String mnc, @Nullable String spn,
+                             @Nullable String imsi, @Nullable String gid1, @Nullable String gid2,
+                             int carrierid, int preciseCarrierId) {
         mMcc = mcc;
         mMnc = mnc;
         mSpn = spn;
         mImsi = imsi;
         mGid1 = gid1;
         mGid2 = gid2;
+        mCarrierId = carrierid;
+        mPreciseCarrierId = preciseCarrierId;
     }
 
     /**
@@ -125,6 +154,22 @@
         return mGid2;
     }
 
+    /**
+     * Get the carrier id {@link TelephonyManager#getSimCarrierId() }
+     * @hide
+     */
+    public int getCarrierId() {
+        return mCarrierId;
+    }
+
+    /**
+     * Get the precise carrier id {@link TelephonyManager#getSimPreciseCarrierId()}
+     * @hide
+     */
+    public int getPreciseCarrierId() {
+        return mPreciseCarrierId;
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (this == obj) {
@@ -140,19 +185,14 @@
                 && Objects.equals(mSpn, that.mSpn)
                 && Objects.equals(mImsi, that.mImsi)
                 && Objects.equals(mGid1, that.mGid1)
-                && Objects.equals(mGid2, that.mGid2);
+                && Objects.equals(mGid2, that.mGid2)
+                && Objects.equals(mCarrierId, that.mCarrierId)
+                && Objects.equals(mPreciseCarrierId, that.mPreciseCarrierId);
     }
 
     @Override
-    public int hashCode() {
-        int result = 1;
-        result = 31 * result + Objects.hashCode(mMcc);
-        result = 31 * result + Objects.hashCode(mMnc);
-        result = 31 * result + Objects.hashCode(mSpn);
-        result = 31 * result + Objects.hashCode(mImsi);
-        result = 31 * result + Objects.hashCode(mGid1);
-        result = 31 * result + Objects.hashCode(mGid2);
-        return result;
+    public int hashCode(){
+        return Objects.hash(mMcc, mMnc, mSpn, mImsi, mGid1, mGid2, mCarrierId, mPreciseCarrierId);
     }
 
     @Override
@@ -168,18 +208,22 @@
         out.writeString(mImsi);
         out.writeString(mGid1);
         out.writeString(mGid2);
+        out.writeInt(mCarrierId);
+        out.writeInt(mPreciseCarrierId);
     }
 
     @Override
     public String toString() {
       return "CarrierIdentifier{"
-          + "mcc=" + mMcc
-          + ",mnc=" + mMnc
-          + ",spn=" + mSpn
-          + ",imsi=" + mImsi
-          + ",gid1=" + mGid1
-          + ",gid2=" + mGid2
-          + "}";
+              + "mcc=" + mMcc
+              + ",mnc=" + mMnc
+              + ",spn=" + mSpn
+              + ",imsi=" + mImsi
+              + ",gid1=" + mGid1
+              + ",gid2=" + mGid2
+              + ",carrierid=" + mCarrierId
+              + ",mPreciseCarrierId=" + mPreciseCarrierId
+              + "}";
     }
 
     /** @hide */
@@ -190,6 +234,8 @@
         mImsi = in.readString();
         mGid1 = in.readString();
         mGid2 = in.readString();
+        mCarrierId = in.readInt();
+        mPreciseCarrierId = in.readInt();
     }
 
     /** @hide */
diff --git a/core/java/android/service/carrier/CarrierService.java b/core/java/android/service/carrier/CarrierService.java
index b94ccf9..c351d89 100644
--- a/core/java/android/service/carrier/CarrierService.java
+++ b/core/java/android/service/carrier/CarrierService.java
@@ -93,7 +93,11 @@
      * </p>
      *
      * @param id contains details about the current carrier that can be used do decide what
-     *            configuration values to return.
+     *           configuration values to return. Instead of using details like MCCMNC to decide
+     *           current carrier, it also contains subscription carrier id
+     *           {@link android.telephony.TelephonyManager#getSimCarrierId()}, a platform-wide
+     *           unique identifier for each carrier, CarrierConfigService can directly use carrier
+     *           id as the key to look up the carrier info.
      * @return a {@link PersistableBundle} object containing the configuration or null if default
      *         values should be used.
      */
diff --git a/core/java/android/service/wallpaper/IWallpaperConnection.aidl b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
index 3c3ef0c..a976d0e 100644
--- a/core/java/android/service/wallpaper/IWallpaperConnection.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
@@ -24,8 +24,8 @@
  * @hide
  */
 interface IWallpaperConnection {
-	void attachEngine(IWallpaperEngine engine);
-	void engineShown(IWallpaperEngine engine);
+    void attachEngine(IWallpaperEngine engine, int displayId);
+    void engineShown(IWallpaperEngine engine);
     ParcelFileDescriptor setWallpaper(String name);
     void onWallpaperColorsChanged(in WallpaperColors colors);
 }
diff --git a/core/java/android/service/wallpaper/IWallpaperService.aidl b/core/java/android/service/wallpaper/IWallpaperService.aidl
index 5fd0157..99a81f5 100644
--- a/core/java/android/service/wallpaper/IWallpaperService.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperService.aidl
@@ -24,6 +24,6 @@
  */
 oneway interface IWallpaperService {
     void attach(IWallpaperConnection connection,
-    		IBinder windowToken, int windowType, boolean isPreview,
-    		int reqWidth, int reqHeight, in Rect padding);
+            IBinder windowToken, int windowType, boolean isPreview,
+            int reqWidth, int reqHeight, in Rect padding, int displayId);
 }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 4bd86a4..6f51bec 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -24,7 +24,6 @@
 import android.app.WallpaperColors;
 import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
-import android.content.Context;
 import android.content.Intent;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
@@ -806,7 +805,7 @@
                                 com.android.internal.R.style.Animation_Wallpaper;
                         mInputChannel = new InputChannel();
                         if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
-                                Display.DEFAULT_DISPLAY, mWinFrame, mContentInsets, mStableInsets,
+                                mDisplay.getDisplayId(), mWinFrame, mContentInsets, mStableInsets,
                                 mOutsets, mDisplayCutout, mInputChannel) < 0) {
                             Log.w(TAG, "Failed to add window while updating wallpaper surface.");
                             return;
@@ -1015,7 +1014,15 @@
             if (mDestroyed) {
                 return;
             }
-            
+
+            mDisplayManager = getSystemService(DisplayManager.class);
+            mDisplay = mDisplayManager.getDisplay(wrapper.mDisplayId);
+            if (mDisplay == null) {
+                // TODO(b/115486823) Ignore this engine.
+                Log.e(TAG, "Attaching to a non-existent display: " + wrapper.mDisplayId);
+                return;
+            }
+
             mIWallpaperEngine = wrapper;
             mCaller = wrapper.mCaller;
             mConnection = wrapper.mConnection;
@@ -1027,10 +1034,7 @@
             mWindow.setSession(mSession);
 
             mLayout.packageName = getPackageName();
-
-            mDisplayManager = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE);
             mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler());
-            mDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
             mDisplayState = mDisplay.getState();
 
             if (DEBUG) Log.v(TAG, "onCreate(): " + this);
@@ -1268,12 +1272,14 @@
         int mReqWidth;
         int mReqHeight;
         final Rect mDisplayPadding = new Rect();
+        int mDisplayId;
 
         Engine mEngine;
 
         IWallpaperEngineWrapper(WallpaperService context,
                 IWallpaperConnection conn, IBinder windowToken,
-                int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) {
+                int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding,
+                int displayId) {
             mCaller = new HandlerCaller(context, context.getMainLooper(), this, true);
             mConnection = conn;
             mWindowToken = windowToken;
@@ -1282,6 +1288,7 @@
             mReqWidth = reqWidth;
             mReqHeight = reqHeight;
             mDisplayPadding.set(padding);
+            mDisplayId = displayId;
             
             Message msg = mCaller.obtainMessage(DO_ATTACH);
             mCaller.sendMessage(msg);
@@ -1353,7 +1360,7 @@
             switch (message.what) {
                 case DO_ATTACH: {
                     try {
-                        mConnection.attachEngine(this);
+                        mConnection.attachEngine(this, mDisplayId);
                     } catch (RemoteException e) {
                         Log.w(TAG, "Wallpaper host disappeared", e);
                         return;
@@ -1453,9 +1460,10 @@
 
         @Override
         public void attach(IWallpaperConnection conn, IBinder windowToken,
-                int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) {
+                int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding,
+                int displayId) {
             new IWallpaperEngineWrapper(mTarget, conn, windowToken,
-                    windowType, isPreview, reqWidth, reqHeight, padding);
+                    windowType, isPreview, reqWidth, reqHeight, padding, displayId);
         }
     }
 
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 5b1544b..c6155ce 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -390,7 +390,8 @@
                 com.android.internal.R.bool.config_enableHapticTextHandle);
 
         if (FLAG_USE_MAGNIFIER) {
-            final Magnifier magnifier = new Magnifier.Builder(mTextView).build();
+            final Magnifier magnifier =
+                    Magnifier.createBuilderWithOldMagnifierDefaults(mTextView).build();
             mMagnifierAnimator = new MagnifierMotionAnimator(magnifier);
         }
     }
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 932f182..f4c25c3 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -145,7 +145,47 @@
      */
     @Deprecated
     public Magnifier(@NonNull View view) {
-        this(new Builder(view));
+        this(createBuilderWithOldMagnifierDefaults(view));
+    }
+
+    static Builder createBuilderWithOldMagnifierDefaults(final View view) {
+        final Builder params = new Builder(view);
+        final Context context = view.getContext();
+        final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Magnifier,
+                R.attr.magnifierStyle, 0);
+        params.mWidth = a.getDimensionPixelSize(R.styleable.Magnifier_magnifierWidth, 0);
+        params.mHeight = a.getDimensionPixelSize(R.styleable.Magnifier_magnifierHeight, 0);
+        params.mElevation = a.getDimension(R.styleable.Magnifier_magnifierElevation, 0);
+        params.mCornerRadius = getDeviceDefaultDialogCornerRadius(context);
+        params.mZoom = a.getFloat(R.styleable.Magnifier_magnifierZoom, 0);
+        params.mHorizontalDefaultSourceToMagnifierOffset =
+                a.getDimensionPixelSize(R.styleable.Magnifier_magnifierHorizontalOffset, 0);
+        params.mVerticalDefaultSourceToMagnifierOffset =
+                a.getDimensionPixelSize(R.styleable.Magnifier_magnifierVerticalOffset, 0);
+        params.mOverlay = new ColorDrawable(a.getColor(
+                R.styleable.Magnifier_magnifierColorOverlay, Color.TRANSPARENT));
+        a.recycle();
+        params.mForcePositionWithinWindowSystemInsetsBounds = true;
+        params.mLeftContentBound = SOURCE_BOUND_MAX_VISIBLE;
+        params.mTopContentBound = SOURCE_BOUND_MAX_IN_SURFACE;
+        params.mRightContentBound = SOURCE_BOUND_MAX_VISIBLE;
+        params.mBottomContentBound = SOURCE_BOUND_MAX_IN_SURFACE;
+        return params;
+    }
+
+    /**
+     * Returns the device default theme dialog corner radius attribute.
+     * We retrieve this from the device default theme to avoid
+     * using the values set in the custom application themes.
+     */
+    private static float getDeviceDefaultDialogCornerRadius(final Context context) {
+        final Context deviceDefaultContext =
+                new ContextThemeWrapper(context, R.style.Theme_DeviceDefault);
+        final TypedArray ta = deviceDefaultContext.obtainStyledAttributes(
+                new int[]{android.R.attr.dialogCornerRadius});
+        final float dialogCornerRadius = ta.getDimension(0, 0);
+        ta.recycle();
+        return dialogCornerRadius;
     }
 
     private Magnifier(@NonNull Builder params) {
@@ -1105,41 +1145,23 @@
         }
 
         private void applyDefaults() {
-            final Context context = mView.getContext();
-            final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Magnifier,
-                    R.attr.magnifierStyle, 0);
-            mWidth = a.getDimensionPixelSize(R.styleable.Magnifier_magnifierWidth, 0);
-            mHeight = a.getDimensionPixelSize(R.styleable.Magnifier_magnifierHeight, 0);
-            mElevation = a.getDimension(R.styleable.Magnifier_magnifierElevation, 0);
-            mCornerRadius = getDeviceDefaultDialogCornerRadius();
-            mZoom = a.getFloat(R.styleable.Magnifier_magnifierZoom, 0);
+            final Resources resources = mView.getContext().getResources();
+            mWidth = resources.getDimensionPixelSize(R.dimen.default_magnifier_width);
+            mHeight = resources.getDimensionPixelSize(R.dimen.default_magnifier_height);
+            mElevation = resources.getDimension(R.dimen.default_magnifier_elevation);
+            mCornerRadius = resources.getDimension(R.dimen.default_magnifier_corner_radius);
+            mZoom = resources.getFloat(R.dimen.default_magnifier_zoom);
             mHorizontalDefaultSourceToMagnifierOffset =
-                    a.getDimensionPixelSize(R.styleable.Magnifier_magnifierHorizontalOffset, 0);
+                    resources.getDimensionPixelSize(R.dimen.default_magnifier_horizontal_offset);
             mVerticalDefaultSourceToMagnifierOffset =
-                    a.getDimensionPixelSize(R.styleable.Magnifier_magnifierVerticalOffset, 0);
-            mOverlay = new ColorDrawable(a.getColor(
-                    R.styleable.Magnifier_magnifierColorOverlay, Color.TRANSPARENT));
-            a.recycle();
+                    resources.getDimensionPixelSize(R.dimen.default_magnifier_vertical_offset);
+            mOverlay = new ColorDrawable(resources.getColor(
+                    R.color.default_magnifier_color_overlay, null));
             mForcePositionWithinWindowSystemInsetsBounds = true;
             mLeftContentBound = SOURCE_BOUND_MAX_VISIBLE;
-            mTopContentBound = SOURCE_BOUND_MAX_IN_SURFACE;
+            mTopContentBound = SOURCE_BOUND_MAX_VISIBLE;
             mRightContentBound = SOURCE_BOUND_MAX_VISIBLE;
-            mBottomContentBound = SOURCE_BOUND_MAX_IN_SURFACE;
-        }
-
-        /**
-         * Returns the device default theme dialog corner radius attribute.
-         * We retrieve this from the device default theme to avoid
-         * using the values set in the custom application themes.
-         */
-        private float getDeviceDefaultDialogCornerRadius() {
-            final Context deviceDefaultContext =
-                    new ContextThemeWrapper(mView.getContext(), R.style.Theme_DeviceDefault);
-            final TypedArray ta = deviceDefaultContext.obtainStyledAttributes(
-                    new int[]{android.R.attr.dialogCornerRadius});
-            final float dialogCornerRadius = ta.getDimension(0, 0);
-            ta.recycle();
-            return dialogCornerRadius;
+            mBottomContentBound = SOURCE_BOUND_MAX_VISIBLE;
         }
 
         /**
@@ -1186,8 +1208,7 @@
         }
 
         /**
-         * Sets the corner radius of the magnifier window, in pixels.
-         * Defaults to the corner radius defined in the device default theme.
+         * Sets the corner radius of the magnifier window, in pixels. Defaults to 2dp.
          * @param cornerRadius the corner radius to be set
          */
         @NonNull
@@ -1201,10 +1222,11 @@
         /**
          * Sets an overlay that will be drawn on the top of the magnifier content.
          * In general, the overlay should not be opaque, in order to let the expected magnifier
-         * content be partially visible. The default overlay is a white {@link ColorDrawable},
-         * with 5% alpha, aiming to make the magnifier distinguishable when shown in dark
-         * application regions. To disable this default (or in general to have no overlay), the
-         * parameter should be set to {@code null}. The overlay will be automatically redrawn
+         * content be partially visible. The default overlay is {@code null} (no overlay).
+         * As an example, TextView applies a white {@link ColorDrawable} overlay with
+         * 5% alpha, aiming to make the magnifier distinguishable when shown in dark
+         * application regions. To disable the overlay, the parameter should be set
+         * to {@code null}. If not null, the overlay will be automatically redrawn
          * when the drawable is invalidated. To achieve this, the magnifier will set a new
          * {@link android.graphics.drawable.Drawable.Callback} for the overlay drawable,
          * so keep in mind that any existing one set by the application will be lost.
@@ -1220,7 +1242,7 @@
          * Sets an offset that should be added to the content source center to obtain
          * the position of the magnifier window, when the {@link #show(float, float)}
          * method is called. The offset is ignored when {@link #show(float, float, float, float)}
-         * is used. The offset can be negative, and it defaults to (0dp, -42dp).
+         * is used. The offset can be negative. It defaults to (0dp, 0dp).
          * @param horizontalOffset the horizontal component of the offset
          * @param verticalOffset the vertical component of the offset
          */
@@ -1406,8 +1428,8 @@
         final Resources resources = Resources.getSystem();
         final float density = resources.getDisplayMetrics().density;
         final PointF size = new PointF();
-        size.x = resources.getDimension(R.dimen.magnifier_width) / density;
-        size.y = resources.getDimension(R.dimen.magnifier_height) / density;
+        size.x = resources.getDimension(R.dimen.default_magnifier_width) / density;
+        size.y = resources.getDimension(R.dimen.default_magnifier_height) / density;
         return size;
     }
 
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index ade5d05..3861739 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -29,6 +29,7 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
+import java.util.function.Predicate;
 
 /**
  * Given a process, will iterate over the child threads of the process, and return the CPU usage
@@ -70,6 +71,11 @@
     private static final String THREAD_NAME_FILENAME = "comm";
 
     /**
+     * Glob pattern for the process directory names under {@code proc}
+     */
+    private static final String PROCESS_DIRECTORY_FILTER = "[0-9]*";
+
+    /**
      * Default process name when the name can't be read
      */
     private static final String DEFAULT_PROCESS_NAME = "unknown_process";
@@ -96,6 +102,18 @@
     private static final int NUM_BUCKETS = 8;
 
     /**
+     * Default predicate for what UIDs to check for when getting processes. This filters to only
+     * select system UIDs (1000-1999)
+     */
+    private static final Predicate<Integer> DEFAULT_UID_PREDICATE =
+            uid -> 1000 <= uid && uid < 2000;
+
+    /**
+     * Value returned when there was an error getting an integer ID value (e.g. PID, UID)
+     */
+    private static final int ID_ERROR = -1;
+
+    /**
      * Where the proc filesystem is mounted
      */
     private final Path mProcPath;
@@ -116,8 +134,13 @@
      */
     private final FrequencyBucketCreator mFrequencyBucketCreator;
 
+    private final Injector mInjector;
+
     private KernelCpuThreadReader() throws IOException {
-        this(DEFAULT_PROC_PATH, DEFAULT_INITIAL_TIME_IN_STATE_PATH);
+        this(
+                DEFAULT_PROC_PATH,
+                DEFAULT_INITIAL_TIME_IN_STATE_PATH,
+                new Injector());
     }
 
     /**
@@ -128,9 +151,13 @@
      * format
      */
     @VisibleForTesting
-    public KernelCpuThreadReader(Path procPath, Path initialTimeInStatePath) throws IOException {
+    public KernelCpuThreadReader(
+            Path procPath,
+            Path initialTimeInStatePath,
+            Injector injector) throws IOException {
         mProcPath = procPath;
         mProcTimeInStateReader = new ProcTimeInStateReader(initialTimeInStatePath);
+        mInjector = injector;
 
         // Copy mProcTimeInState's frequencies and initialize bucketing
         final long[] frequenciesKhz = mProcTimeInStateReader.getFrequenciesKhz();
@@ -154,6 +181,67 @@
     }
 
     /**
+     * Get the per-thread CPU usage of all processes belonging to UIDs between {@code [1000, 2000)}
+     */
+    @Nullable
+    public ArrayList<ProcessCpuUsage> getProcessCpuUsageByUids() {
+        return getProcessCpuUsageByUids(DEFAULT_UID_PREDICATE);
+    }
+
+    /**
+     * Get the per-thread CPU usage of all processes belonging to a set of UIDs
+     *
+     * <p>This function will crawl through all process {@code proc} directories found by the pattern
+     * {@code /proc/[0-9]*}, and then check the UID using {@code /proc/$PID/status}. This takes
+     * approximately 500ms on a Pixel 2. Therefore, this method can be computationally expensive,
+     * and should not be called more than once an hour.
+     *
+     * @param uidPredicate only get usage from processes owned by UIDs that match this predicate
+     */
+    @Nullable
+    public ArrayList<ProcessCpuUsage> getProcessCpuUsageByUids(Predicate<Integer> uidPredicate) {
+        if (DEBUG) {
+            Slog.d(TAG, "Reading CPU thread usages for processes owned by UIDs");
+        }
+
+        final ArrayList<ProcessCpuUsage> processCpuUsages = new ArrayList<>();
+
+        try (DirectoryStream<Path> processPaths =
+                     Files.newDirectoryStream(mProcPath, PROCESS_DIRECTORY_FILTER)) {
+            for (Path processPath : processPaths) {
+                final int processId = getProcessId(processPath);
+                final int uid = mInjector.getUidForPid(processId);
+                if (uid == ID_ERROR || processId == ID_ERROR) {
+                    continue;
+                }
+                if (!uidPredicate.test(uid)) {
+                    continue;
+                }
+
+                final ProcessCpuUsage processCpuUsage =
+                        getProcessCpuUsage(processPath, processId, uid);
+                if (processCpuUsage != null) {
+                    processCpuUsages.add(processCpuUsage);
+                }
+            }
+        } catch (IOException e) {
+            Slog.w("Failed to iterate over process paths", e);
+            return null;
+        }
+
+        if (processCpuUsages.isEmpty()) {
+            Slog.w(TAG, "Didn't successfully get any process CPU information for UIDs specified");
+            return null;
+        }
+
+        if (DEBUG) {
+            Slog.d(TAG, "Read usage for " + processCpuUsages.size() + " processes");
+        }
+
+        return processCpuUsages;
+    }
+
+    /**
      * Read all of the CPU usage statistics for each child thread of the current process
      *
      * @return process CPU usage containing usage of all child threads
@@ -162,8 +250,8 @@
     public ProcessCpuUsage getCurrentProcessCpuUsage() {
         return getProcessCpuUsage(
                 mProcPath.resolve("self"),
-                Process.myPid(),
-                Process.myUid());
+                mInjector.myPid(),
+                mInjector.myUid());
     }
 
     /**
@@ -172,7 +260,8 @@
      * @param processPath the {@code /proc} path of the thread
      * @param processId the ID of the process
      * @param uid the ID of the user who owns the process
-     * @return process CPU usage containing usage of all child threads
+     * @return process CPU usage containing usage of all child threads. Null if the process exited
+     * and its {@code proc} directory was removed while collecting information
      */
     @Nullable
     private ProcessCpuUsage getProcessCpuUsage(Path processPath, int processId, int uid) {
@@ -224,7 +313,8 @@
      * Get a thread's CPU usage
      *
      * @param threadDirectory the {@code /proc} directory of the thread
-     * @return null in the case that the directory read failed
+     * @return thread CPU usage. Null if the thread exited and its {@code proc} directory was
+     * removed while collecting information
      */
     @Nullable
     private ThreadCpuUsage getThreadCpuUsage(Path threadDirectory) {
@@ -280,6 +370,22 @@
     }
 
     /**
+     * Get the ID of a process from its path
+     *
+     * @param processPath {@code proc} path of the process
+     * @return the ID, {@link #ID_ERROR} if the path could not be parsed
+     */
+    private int getProcessId(Path processPath) {
+        String fileName = processPath.getFileName().toString();
+        try {
+            return Integer.parseInt(fileName);
+        } catch (NumberFormatException e) {
+            Slog.w(TAG, "Failed to parse " + fileName + " as process ID", e);
+            return ID_ERROR;
+        }
+    }
+
+    /**
      * Puts frequencies and usage times into buckets
      */
     @VisibleForTesting
@@ -443,4 +549,31 @@
             this.usageTimesMillis = usageTimesMillis;
         }
     }
+
+    /**
+     * Used to inject static methods from {@link Process}
+     */
+    @VisibleForTesting
+    public static class Injector {
+        /**
+         * Get the PID of the current process
+         */
+        public int myPid() {
+            return Process.myPid();
+        }
+
+        /**
+         * Get the UID that owns the current process
+         */
+        public int myUid() {
+            return Process.myUid();
+        }
+
+        /**
+         * Get the UID for the process with ID {@code pid}
+         */
+        public int getUidForPid(int pid) {
+            return Process.getUidForPid(pid);
+        }
+    }
 }
diff --git a/core/java/com/android/internal/os/ProcTimeInStateReader.java b/core/java/com/android/internal/os/ProcTimeInStateReader.java
index 3a63498..2318473 100644
--- a/core/java/com/android/internal/os/ProcTimeInStateReader.java
+++ b/core/java/com/android/internal/os/ProcTimeInStateReader.java
@@ -19,9 +19,15 @@
 import android.annotation.Nullable;
 import android.os.Process;
 
+import com.android.internal.util.ArrayUtils;
+
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Reads and parses {@code time_in_state} files in the {@code proc} filesystem.
@@ -43,6 +49,17 @@
  *
  * This file would indicate that the CPU has spent 30 milliseconds at frequency 300,000KHz (300Mhz)
  * and 10 milliseconds at frequency 1,900,800KHz (1.9GHz).
+ *
+ * <p>This class will also read {@code time_in_state} files with headers, such as:
+ * <pre>
+ *   cpu0
+ *   300000 3
+ *   364800 0
+ *   ...
+ *   cpu4
+ *   300000 1
+ *   364800 4
+ * </pre>
  */
 public class ProcTimeInStateReader {
     private static final String TAG = "ProcTimeInStateReader";
@@ -51,24 +68,28 @@
      * The format of a single line of the {@code time_in_state} file that exports the frequency
      * values
      */
-    private static final int[] TIME_IN_STATE_LINE_FREQUENCY_FORMAT = {
+    private static final List<Integer> TIME_IN_STATE_LINE_FREQUENCY_FORMAT = Arrays.asList(
             Process.PROC_OUT_LONG | Process.PROC_SPACE_TERM,
-            Process.PROC_NEWLINE_TERM,
-    };
+            Process.PROC_NEWLINE_TERM
+    );
 
     /**
      * The format of a single line of the {@code time_in_state} file that exports the time values
      */
-    private static final int[] TIME_IN_STATE_LINE_TIME_FORMAT = {
+    private static final List<Integer> TIME_IN_STATE_LINE_TIME_FORMAT = Arrays.asList(
             Process.PROC_SPACE_TERM,
-            Process.PROC_OUT_LONG | Process.PROC_NEWLINE_TERM,
-    };
+            Process.PROC_OUT_LONG | Process.PROC_NEWLINE_TERM
+    );
 
     /**
-     * The format of the {@code time_in_state} file, defined using {@link Process}'s {@code
-     * PROC_OUT_LONG} and related variables
-     *
-     * Defined on first successful read of {@code time_in_state} file.
+     * The format of a header line of the {@code time_in_state} file
+     */
+    private static final List<Integer> TIME_IN_STATE_HEADER_LINE_FORMAT =
+            Collections.singletonList(Process.PROC_NEWLINE_TERM);
+
+    /**
+     * The format of the {@code time_in_state} file to extract times, defined using {@link
+     * Process}'s {@code PROC_OUT_LONG} and related variables
      */
     private int[] mTimeInStateTimeFormat;
 
@@ -141,46 +162,44 @@
         // Read the bytes of the `time_in_state` file
         byte[] timeInStateBytes = Files.readAllBytes(timeInStatePath);
 
-        // The number of lines in the `time_in_state` file is the number of frequencies available
+        // Iterate over the lines of the time_in_state file, for each one adding a line to the
+        // formats. These formats are used to extract either the frequencies or the times from a
+        // time_in_state file
+        // Also check if each line is a header, and handle this in the created format arrays
+        ArrayList<Integer> timeInStateFrequencyFormat = new ArrayList<>();
+        ArrayList<Integer> timeInStateTimeFormat = new ArrayList<>();
         int numFrequencies = 0;
         for (int i = 0; i < timeInStateBytes.length; i++) {
-            if (timeInStateBytes[i] == '\n') {
+            // If the first character of the line is not a digit, we treat it as a header
+            if (!Character.isDigit(timeInStateBytes[i])) {
+                timeInStateFrequencyFormat.addAll(TIME_IN_STATE_HEADER_LINE_FORMAT);
+                timeInStateTimeFormat.addAll(TIME_IN_STATE_HEADER_LINE_FORMAT);
+            } else {
+                timeInStateFrequencyFormat.addAll(TIME_IN_STATE_LINE_FREQUENCY_FORMAT);
+                timeInStateTimeFormat.addAll(TIME_IN_STATE_LINE_TIME_FORMAT);
                 numFrequencies++;
             }
-        }
-        if (numFrequencies == 0) {
-            throw new IOException("Empty time_in_state file");
+            // Go to the next line
+            while (i < timeInStateBytes.length && timeInStateBytes[i] != '\n') {
+                i++;
+            }
         }
 
-        // Set `mTimeInStateTimeFormat` and `timeInStateFrequencyFormat` to the correct length, and
-        // then copy in the `TIME_IN_STATE_{FREQUENCY,TIME}_LINE_FORMAT` until it's full. As we only
-        // use the frequency format in this method, it is not an member variable.
-        final int[] timeInStateTimeFormat =
-                new int[numFrequencies * TIME_IN_STATE_LINE_TIME_FORMAT.length];
-        final int[] timeInStateFrequencyFormat =
-                new int[numFrequencies * TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length];
-        for (int i = 0; i < numFrequencies; i++) {
-            System.arraycopy(
-                    TIME_IN_STATE_LINE_FREQUENCY_FORMAT, 0, timeInStateFrequencyFormat,
-                    i * TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length,
-                    TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length);
-            System.arraycopy(
-                    TIME_IN_STATE_LINE_TIME_FORMAT, 0, timeInStateTimeFormat,
-                    i * TIME_IN_STATE_LINE_TIME_FORMAT.length,
-                    TIME_IN_STATE_LINE_TIME_FORMAT.length);
+        if (numFrequencies == 0) {
+            throw new IOException("Empty time_in_state file");
         }
 
         // Read the frequencies from the `time_in_state` file and store them, as they will be the
         // same for every `time_in_state` file
         final long[] readLongs = new long[numFrequencies];
         final boolean readSuccess = Process.parseProcLine(
-                timeInStateBytes, 0, timeInStateBytes.length, timeInStateFrequencyFormat,
-                null, readLongs, null);
+                timeInStateBytes, 0, timeInStateBytes.length,
+                ArrayUtils.convertToIntArray(timeInStateFrequencyFormat), null, readLongs, null);
         if (!readSuccess) {
             throw new IOException("Failed to parse time_in_state file");
         }
 
-        mTimeInStateTimeFormat = timeInStateTimeFormat;
+        mTimeInStateTimeFormat = ArrayUtils.convertToIntArray(timeInStateTimeFormat);
         mFrequenciesKhz = readLongs;
     }
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 20d315f..e35b701 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4201,7 +4201,13 @@
     <!-- @SystemApi Allows modifying accessibility state.
          @hide -->
     <permission android:name="android.permission.MANAGE_ACCESSIBILITY"
-                android:protectionLevel="signature|setup" />
+        android:protectionLevel="signature|setup" />
+
+    <!-- @SystemApi Allows an app to grant a profile owner access to device identifiers.
+         <p>Not for use by third-party applications.
+         @hide -->
+    <permission android:name="android.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS"
+        android:protectionLevel="signature" />
 
     <!-- Allows financial apps to read filtered sms messages. -->
     <permission android:name="android.permission.SMS_FINANCIAL_TRANSACTIONS"
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index ffcd300..4122cf0 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -211,6 +211,6 @@
     <color name="floating_popup_divider_light">#E9E9E9</color>
 
     <!-- Magnifier -->
-    <color name="magnifier_color_overlay">#0EFFFFFF</color>
+    <color name="default_magnifier_color_overlay">#00FFFFFF</color>
 
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index b65c0fd..e902989 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -578,12 +578,13 @@
     <dimen name="floating_toolbar_icon_text_spacing">8dp</dimen>
 
     <!-- Magnifier dimensions -->
-    <dimen name="magnifier_width">100dp</dimen>
-    <dimen name="magnifier_height">48dp</dimen>
-    <dimen name="magnifier_elevation">4dp</dimen>
-    <dimen name="magnifier_vertical_offset">-42dp</dimen>
-    <dimen name="magnifier_horizontal_offset">0dp</dimen>
-    <item type="dimen" format="float" name="magnifier_zoom">1.25</item>
+    <dimen name="default_magnifier_width">100dp</dimen>
+    <dimen name="default_magnifier_height">48dp</dimen>
+    <dimen name="default_magnifier_elevation">4dp</dimen>
+    <dimen name="default_magnifier_corner_radius">2dp</dimen>
+    <dimen name="default_magnifier_vertical_offset">-42dp</dimen>
+    <dimen name="default_magnifier_horizontal_offset">0dp</dimen>
+    <item type="dimen" format="float" name="default_magnifier_zoom">1.25</item>
 
     <dimen name="chooser_grid_padding">0dp</dimen>
     <!-- Spacing around the background change frome service to non-service -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index ec22f42..bd53936 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -800,13 +800,13 @@
     </style>
 
     <style name="Widget.Magnifier">
-        <item name="magnifierWidth">@dimen/magnifier_width</item>
-        <item name="magnifierHeight">@dimen/magnifier_height</item>
-        <item name="magnifierZoom">@dimen/magnifier_zoom</item>
-        <item name="magnifierElevation">@dimen/magnifier_elevation</item>
-        <item name="magnifierVerticalOffset">@dimen/magnifier_vertical_offset</item>
-        <item name="magnifierHorizontalOffset">@dimen/magnifier_horizontal_offset</item>
-        <item name="magnifierColorOverlay">@color/magnifier_color_overlay</item>
+        <item name="magnifierWidth">100dp</item>
+        <item name="magnifierHeight">48dp</item>
+        <item name="magnifierZoom">1.25</item>
+        <item name="magnifierElevation">4dp</item>
+        <item name="magnifierVerticalOffset">-42dp</item>
+        <item name="magnifierHorizontalOffset">0dp</item>
+        <item name="magnifierColorOverlay">#0EFFFFFF</item>
     </style>
 
     <!-- Text Appearances -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b25e7a8..82c9ff3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2637,13 +2637,14 @@
   <java-symbol type="attr" name="floatingToolbarDividerColor" />
 
   <!-- Magnifier -->
-  <java-symbol type="dimen" name="magnifier_width" />
-  <java-symbol type="dimen" name="magnifier_height" />
-  <java-symbol type="dimen" name="magnifier_elevation" />
-  <java-symbol type="dimen" name="magnifier_zoom" />
-  <java-symbol type="dimen" name="magnifier_vertical_offset" />
-  <java-symbol type="dimen" name="magnifier_horizontal_offset" />
-  <java-symbol type="color" name="magnifier_color_overlay" />
+  <java-symbol type="dimen" name="default_magnifier_width" />
+  <java-symbol type="dimen" name="default_magnifier_height" />
+  <java-symbol type="dimen" name="default_magnifier_elevation" />
+  <java-symbol type="dimen" name="default_magnifier_corner_radius" />
+  <java-symbol type="dimen" name="default_magnifier_zoom" />
+  <java-symbol type="dimen" name="default_magnifier_vertical_offset" />
+  <java-symbol type="dimen" name="default_magnifier_horizontal_offset" />
+  <java-symbol type="color" name="default_magnifier_color_overlay" />
   <java-symbol type="attr" name="magnifierWidth" />
   <java-symbol type="attr" name="magnifierHeight" />
   <java-symbol type="attr" name="magnifierElevation" />
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
index c866bc4..b242a34 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -39,13 +39,16 @@
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Comparator;
+import java.util.function.Predicate;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class KernelCpuThreadReaderTest {
 
-    private static final String PROCESS_NAME = "test_process";
+    private static final int UID = 1000;
+    private static final int PROCESS_ID = 1234;
     private static final int[] THREAD_IDS = {0, 1000, 1235, 4321};
+    private static final String PROCESS_NAME = "test_process";
     private static final String[] THREAD_NAMES = {
             "test_thread_1", "test_thread_2", "test_thread_3", "test_thread_4"
     };
@@ -73,49 +76,126 @@
     }
 
     @Test
-    public void testSimple() throws IOException {
-        // Make /proc/self
-        final Path selfPath = mProcDirectory.toPath().resolve("self");
-        assertTrue(selfPath.toFile().mkdirs());
+    public void testReader_currentProcess() throws IOException {
+        KernelCpuThreadReader.Injector processUtils =
+                new KernelCpuThreadReader.Injector() {
+                    @Override
+                    public int myPid() {
+                        return PROCESS_ID;
+                    }
 
-        // Make /proc/self/task
-        final Path selfThreadsPath = selfPath.resolve("task");
+                    @Override
+                    public int myUid() {
+                        return UID;
+                    }
+
+                    @Override
+                    public int getUidForPid(int pid) {
+                        return 0;
+                    }
+                };
+        setupDirectory(mProcDirectory.toPath().resolve("self"), THREAD_IDS, PROCESS_NAME,
+                THREAD_NAMES, THREAD_CPU_FREQUENCIES, THREAD_CPU_TIMES);
+
+        final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
+                mProcDirectory.toPath(),
+                mProcDirectory.toPath().resolve("self/task/" + THREAD_IDS[0] + "/time_in_state"),
+                processUtils);
+        final KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
+                kernelCpuThreadReader.getCurrentProcessCpuUsage();
+        checkResults(processCpuUsage, kernelCpuThreadReader.getCpuFrequenciesKhz(), UID, PROCESS_ID,
+                THREAD_IDS, PROCESS_NAME, THREAD_NAMES, THREAD_CPU_FREQUENCIES, THREAD_CPU_TIMES);
+    }
+
+    @Test
+    public void testReader_byUids() throws IOException {
+        int[] uids = new int[]{0, 2, 3, 4, 5, 6000};
+        Predicate<Integer> uidPredicate = uid -> uid == 0 || uid >= 4;
+        int[] expectedUids = new int[]{0, 4, 5, 6000};
+        KernelCpuThreadReader.Injector processUtils =
+                new KernelCpuThreadReader.Injector() {
+                    @Override
+                    public int myPid() {
+                        return 0;
+                    }
+
+                    @Override
+                    public int myUid() {
+                        return 0;
+                    }
+
+                    @Override
+                    public int getUidForPid(int pid) {
+                        return pid;
+                    }
+                };
+
+        for (int uid : uids) {
+            setupDirectory(mProcDirectory.toPath().resolve(String.valueOf(uid)),
+                    new int[]{uid * 10},
+                    "process" + uid, new String[]{"thread" + uid}, new int[]{1000},
+                    new int[][]{{uid}});
+        }
+        final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
+                mProcDirectory.toPath(),
+                mProcDirectory.toPath().resolve(uids[0] + "/task/" + uids[0] + "/time_in_state"),
+                processUtils);
+        ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsageByUids =
+                kernelCpuThreadReader.getProcessCpuUsageByUids(uidPredicate);
+        processCpuUsageByUids.sort(Comparator.comparing(usage -> usage.processId));
+
+        assertEquals(expectedUids.length, processCpuUsageByUids.size());
+        for (int i = 0; i < expectedUids.length; i++) {
+            KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
+                    processCpuUsageByUids.get(i);
+            int uid = expectedUids[i];
+            checkResults(processCpuUsage, kernelCpuThreadReader.getCpuFrequenciesKhz(),
+                    uid, uid, new int[]{uid * 10}, "process" + uid, new String[]{"thread" + uid},
+                    new int[]{1000}, new int[][]{{uid}});
+        }
+    }
+
+    private void setupDirectory(Path processPath, int[] threadIds, String processName,
+            String[] threadNames, int[] cpuFrequencies, int[][] cpuTimes) throws IOException {
+        // Make /proc/$PID
+        assertTrue(processPath.toFile().mkdirs());
+
+        // Make /proc/$PID/task
+        final Path selfThreadsPath = processPath.resolve("task");
         assertTrue(selfThreadsPath.toFile().mkdirs());
 
-        // Make /proc/self/cmdline
-        Files.write(selfPath.resolve("cmdline"), PROCESS_NAME.getBytes());
+        // Make /proc/$PID/cmdline
+        Files.write(processPath.resolve("cmdline"), processName.getBytes());
 
         // Make thread directories in reverse order, as they are read in order of creation by
         // CpuThreadProcReader
-        for (int i = 0; i < THREAD_IDS.length; i++) {
-            // Make /proc/self/task/$TID
-            final Path threadPath = selfThreadsPath.resolve(String.valueOf(THREAD_IDS[i]));
+        for (int i = 0; i < threadIds.length; i++) {
+            // Make /proc/$PID/task/$TID
+            final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i]));
             assertTrue(threadPath.toFile().mkdirs());
 
-            // Make /proc/self/task/$TID/comm
-            Files.write(threadPath.resolve("comm"), THREAD_NAMES[i].getBytes());
+            // Make /proc/$PID/task/$TID/comm
+            Files.write(threadPath.resolve("comm"), threadNames[i].getBytes());
 
-            // Make /proc/self/task/$TID/time_in_state
+            // Make /proc/$PID/task/$TID/time_in_state
             final OutputStream timeInStateStream =
                     Files.newOutputStream(threadPath.resolve("time_in_state"));
-            for (int j = 0; j < THREAD_CPU_FREQUENCIES.length; j++) {
-                final String line = String.valueOf(THREAD_CPU_FREQUENCIES[j]) + " "
-                        + String.valueOf(THREAD_CPU_TIMES[i][j]) + "\n";
+            for (int j = 0; j < cpuFrequencies.length; j++) {
+                final String line = String.valueOf(cpuFrequencies[j]) + " "
+                        + String.valueOf(cpuTimes[i][j]) + "\n";
                 timeInStateStream.write(line.getBytes());
             }
             timeInStateStream.close();
         }
+    }
 
-        final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
-                mProcDirectory.toPath(),
-                mProcDirectory.toPath().resolve("self/task/" + THREAD_IDS[0] + "/time_in_state"));
-        final KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
-                kernelCpuThreadReader.getCurrentProcessCpuUsage();
-
+    private void checkResults(KernelCpuThreadReader.ProcessCpuUsage processCpuUsage,
+            int[] readerCpuFrequencies, int uid, int processId, int[] threadIds, String processName,
+            String[] threadNames, int[] cpuFrequencies, int[][] cpuTimes) {
         assertNotNull(processCpuUsage);
-        assertEquals(android.os.Process.myPid(), processCpuUsage.processId);
-        assertEquals(android.os.Process.myUid(), processCpuUsage.uid);
-        assertEquals(PROCESS_NAME, processCpuUsage.processName);
+        assertEquals(processId, processCpuUsage.processId);
+        assertEquals(uid, processCpuUsage.uid);
+        assertEquals(processName, processCpuUsage.processName);
 
         // Sort the thread CPU usages to compare with test case
         final ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages =
@@ -124,21 +204,21 @@
 
         int threadCount = 0;
         for (KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage : threadCpuUsages) {
-            assertEquals(THREAD_IDS[threadCount], threadCpuUsage.threadId);
-            assertEquals(THREAD_NAMES[threadCount], threadCpuUsage.threadName);
+            assertEquals(threadIds[threadCount], threadCpuUsage.threadId);
+            assertEquals(threadNames[threadCount], threadCpuUsage.threadName);
 
             for (int i = 0; i < threadCpuUsage.usageTimesMillis.length; i++) {
                 assertEquals(
-                        THREAD_CPU_TIMES[threadCount][i] * 10,
+                        cpuTimes[threadCount][i] * 10,
                         threadCpuUsage.usageTimesMillis[i]);
                 assertEquals(
-                        THREAD_CPU_FREQUENCIES[i],
-                        kernelCpuThreadReader.getCpuFrequenciesKhz()[i]);
+                        cpuFrequencies[i],
+                        readerCpuFrequencies[i]);
             }
             threadCount++;
         }
 
-        assertEquals(threadCount, THREAD_IDS.length);
+        assertEquals(threadCount, threadIds.length);
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
index f2a531f..2893066 100644
--- a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
@@ -70,6 +70,23 @@
     }
 
     @Test
+    public void testHeaderFormat() throws IOException {
+        final Path initialTimeInStateFile = mProcDirectory.toPath().resolve(
+                "initial-time-in-state");
+        Files.write(initialTimeInStateFile, "header1\n1 2\nheader2:\n3 4\n5 6\n7 8\n".getBytes());
+        final ProcTimeInStateReader reader = new ProcTimeInStateReader(initialTimeInStateFile);
+
+        assertArrayEquals(
+                "Reported frequencies are correct",
+                new long[]{1, 3, 5, 7},
+                reader.getFrequenciesKhz());
+        assertArrayEquals(
+                "Reported usage times are correct",
+                new long[]{20, 40, 60, 80},
+                reader.getUsageTimesMillis(initialTimeInStateFile));
+    }
+
+    @Test
     public void testDifferentFile() throws IOException {
         Path initialTimeInStateFile = mProcDirectory.toPath().resolve("initial-time-in-state");
         Files.write(initialTimeInStateFile, "1 2\n3 4\n5 6\n7 8\n".getBytes());
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 009e042..e3b165c 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -1869,8 +1869,8 @@
         }
 
         float scale = (float) dstDensity / srcDensity;
-        int scaledWidth = (int) (mWidth * scale + 0.5f);
-        int scaledHeight = (int) (mHeight * scale + 0.5f);
+        int scaledWidth = Math.max((int) (mWidth * scale + 0.5f), 1);
+        int scaledHeight = Math.max((int) (mHeight * scale + 0.5f), 1);
         this.setTargetSize(scaledWidth, scaledHeight);
         return dstDensity;
     }
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index 7216a22..072fe73 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -282,8 +282,11 @@
      * Returns {@code true} if the entry no longer exists.
      */
     public static boolean deleteUserKeyTypeForAlias(KeyStore keystore, String alias, int uid) {
-        return keystore.delete(Credentials.USER_PRIVATE_KEY + alias, uid) ||
-                keystore.delete(Credentials.USER_SECRET_KEY + alias, uid);
+        int ret = keystore.delete2(Credentials.USER_PRIVATE_KEY + alias, uid);
+        if (ret == KeyStore.KEY_NOT_FOUND) {
+            return keystore.delete(Credentials.USER_SECRET_KEY + alias, uid);
+        }
+        return ret == KeyStore.NO_ERROR;
     }
 
     /**
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 6d58d95..6e6ed30 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -267,16 +267,20 @@
         }
     }
 
-    public boolean delete(String key, int uid) {
+    int delete2(String key, int uid) {
         try {
-            int ret = mBinder.del(key, uid);
-            return (ret == NO_ERROR || ret == KEY_NOT_FOUND);
+            return mBinder.del(key, uid);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
-            return false;
+            return SYSTEM_ERROR;
         }
     }
 
+    public boolean delete(String key, int uid) {
+        int ret = delete2(key, uid);
+        return ret == NO_ERROR || ret == KEY_NOT_FOUND;
+    }
+
     @UnsupportedAppUsage
     public boolean delete(String key) {
         return delete(key, UID_SELF);
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 9c707bab..9a15ff2 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -30,6 +30,7 @@
 #include <SkColorSpaceXformCanvas.h>
 #include <SkDeque.h>
 #include <SkDrawable.h>
+#include <SkFont.h>
 #include <SkGraphics.h>
 #include <SkImage.h>
 #include <SkImagePriv.h>
@@ -732,6 +733,7 @@
                             float y, float boundsLeft, float boundsTop, float boundsRight,
                             float boundsBottom, float totalAdvance) {
     if (count <= 0 || paint.nothingToDraw()) return;
+    SkFont font = SkFont::LEGACY_ExtractFromPaint(paint);
     SkPaint paintCopy(paint);
     if (mPaintFilter) {
         mPaintFilter->filter(&paintCopy);
@@ -748,7 +750,7 @@
             SkRect::MakeLTRB(boundsLeft + x, boundsTop + y, boundsRight + x, boundsBottom + y);
 
     SkTextBlobBuilder builder;
-    const SkTextBlobBuilder::RunBuffer& buffer = builder.allocRunPos(paintCopy, count, &bounds);
+    const SkTextBlobBuilder::RunBuffer& buffer = builder.allocRunPos(font, count, &bounds);
     glyphFunc(buffer.glyphs, buffer.pos);
 
     sk_sp<SkTextBlob> textBlob(builder.make());
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 2c73940..0331581 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -461,7 +461,7 @@
         ProjectionLayer(int* drawCounter)
                 : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr)
                 , mDrawCounter(drawCounter) {}
-        virtual sk_sp<SkImage> onNewImageSnapshot() override {
+        virtual sk_sp<SkImage> onNewImageSnapshot(const SkIRect* bounds) override {
             EXPECT_EQ(3, (*mDrawCounter)++);
             EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X,
                                        300 - SCROLL_Y),
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 65b4e26..d16b8be 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -247,7 +247,7 @@
 
     SkCanvas* onNewCanvas() override { return new T(); }
     sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { return nullptr; }
-    sk_sp<SkImage> onNewImageSnapshot() override { return nullptr; }
+    sk_sp<SkImage> onNewImageSnapshot(const SkIRect* bounds) override { return nullptr; }
     T* canvas() { return static_cast<T*>(getCanvas()); }
     void onCopyOnWrite(ContentChangeMode) override {}
     void onWritePixels(const SkPixmap&, int x, int y) override {}
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index c5a6ec5..f1d9397 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -28,10 +28,13 @@
         "libgui",
         "libui",
         "libinput",
-        "libinputflinger",
         "libnativewindow",
     ],
 
+    header_libs: [
+        "libinputflinger_headers",
+    ],
+
     include_dirs: ["frameworks/native/services"],
 
     cflags: [
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index eb3469e..7f4e5a5 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -24,7 +24,7 @@
 
 #include <ui/DisplayInfo.h>
 #include <input/Input.h>
-#include <inputflinger/PointerControllerInterface.h>
+#include <PointerControllerInterface.h>
 #include <utils/BitSet.h>
 #include <utils/RefBase.h>
 #include <utils/Looper.h>
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 548dc88..8288976 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -1763,10 +1763,13 @@
      */
     // This is an asynchronous call.
     public Object setAudioSessionId(int sessionId) {
-        keepAudioSessionIdAlive(sessionId);
+        final AudioTrack dummyAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100,
+                    AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, 2,
+                    AudioTrack.MODE_STATIC, sessionId);
         return addTask(new Task(CALL_COMPLETED_SET_AUDIO_SESSION_ID, false) {
             @Override
             void process() {
+                keepAudioSessionIdAlive(dummyAudioTrack);
                 native_setAudioSessionId(sessionId);
             }
         });
@@ -4621,4 +4624,17 @@
                     AudioTrack.MODE_STATIC, sessionId);
         }
     }
+
+    private void keepAudioSessionIdAlive(AudioTrack at) {
+        synchronized (mSessionIdLock) {
+            if (mDummyAudioTrack != null) {
+                if (mDummyAudioTrack.getAudioSessionId() == at.getAudioSessionId()) {
+                    at.release();
+                    return;
+                }
+                mDummyAudioTrack.release();
+            }
+            mDummyAudioTrack = at;
+        }
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 47e51f3..08a75ab 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -22,12 +22,11 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
-import static org.robolectric.Shadows.shadowOf;
 
 import android.app.ActivityManager;
 import android.content.ContentResolver;
@@ -54,7 +53,6 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowAudioManager;
 import org.robolectric.shadows.ShadowSettings;
 
 import java.util.HashMap;
@@ -72,7 +70,7 @@
     private static final String PERCENTAGE_50 = "50%";
     private static final String PERCENTAGE_100 = "100%";
 
-    private ShadowAudioManager mShadowAudioManager;
+    private AudioManager mAudioManager;
     private Context mContext;
     @Mock
     private LocationManager mLocationManager;
@@ -85,7 +83,7 @@
         mContext = spy(RuntimeEnvironment.application);
         when(mContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager);
         ShadowSecure.reset();
-        mShadowAudioManager = shadowOf(mContext.getSystemService(AudioManager.class));
+        mAudioManager = mContext.getSystemService(AudioManager.class);
     }
 
     @Test
@@ -205,28 +203,28 @@
 
     @Test
     public void isAudioModeOngoingCall_modeInCommunication_returnTrue() {
-        mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+        mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
 
         assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
     }
 
     @Test
     public void isAudioModeOngoingCall_modeInCall_returnTrue() {
-        mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
+        mAudioManager.setMode(AudioManager.MODE_IN_CALL);
 
         assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
     }
 
     @Test
     public void isAudioModeOngoingCall_modeRingtone_returnTrue() {
-        mShadowAudioManager.setMode(AudioManager.MODE_RINGTONE);
+        mAudioManager.setMode(AudioManager.MODE_RINGTONE);
 
         assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
     }
 
     @Test
     public void isAudioModeOngoingCall_modeNormal_returnFalse() {
-        mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+        mAudioManager.setMode(AudioManager.MODE_NORMAL);
 
         assertThat(Utils.isAudioModeOngoingCall(mContext)).isFalse();
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 49520c0..5ceede1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -17,15 +17,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
-import static org.robolectric.Shadows.shadowOf;
 
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
@@ -40,7 +39,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowAudioManager;
 
 @RunWith(SettingsLibRobolectricTestRunner.class)
 public class CachedBluetoothDeviceTest {
@@ -67,7 +65,7 @@
     @Mock
     private BluetoothDevice mSubDevice;
     private CachedBluetoothDevice mCachedDevice;
-    private ShadowAudioManager mShadowAudioManager;
+    private AudioManager mAudioManager;
     private Context mContext;
     private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
 
@@ -75,7 +73,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = RuntimeEnvironment.application;
-        mShadowAudioManager = shadowOf(mContext.getSystemService(AudioManager.class));
+        mAudioManager = mContext.getSystemService(AudioManager.class);
         when(mDevice.getAddress()).thenReturn(DEVICE_ADDRESS);
         when(mHfpProfile.isProfileReady()).thenReturn(true);
         when(mA2dpProfile.isProfileReady()).thenReturn(true);
@@ -212,7 +210,7 @@
         //   2. Audio Manager: In Call
         updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
-        mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
+        mAudioManager.setMode(AudioManager.MODE_IN_CALL);
 
         // Act & Assert:
         //   Get null result without Battery Level.
@@ -228,7 +226,7 @@
         updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
         mBatteryLevel = 10;
-        mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
+        mAudioManager.setMode(AudioManager.MODE_IN_CALL);
 
         // Act & Assert:
         //   Get "10% battery" result with Battery Level 10.
@@ -244,14 +242,13 @@
 
         // Set device as Active for HFP and test connection state summary
         mCachedDevice.onAudioModeChanged();
-        mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
+        mAudioManager.setMode(AudioManager.MODE_IN_CALL);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
 
         // Test with battery level
         mBatteryLevel = 10;
-        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
-                "Active, 10% battery");
+        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active, 10% battery");
 
         // Set HFP profile to be disconnected and test connection state summary
         updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -363,7 +360,7 @@
         //   2. Audio Manager: In Call
         updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
-        mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
+        mAudioManager.setMode(AudioManager.MODE_IN_CALL);
 
         // Act & Assert:
         //   Get "Active" result without Battery Level.
@@ -378,8 +375,8 @@
         //   3. Audio Manager: In Call
         updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+        mAudioManager.setMode(AudioManager.MODE_IN_CALL);
         mBatteryLevel = 10;
-        mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
 
         // Act & Assert:
         //   Get "Active, 10% battery" result with Battery Level 10.
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index e0c4d72..2227642 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -185,6 +185,8 @@
     private static final Bundle NULL_SETTING_BUNDLE = Bundle.forPair(
             Settings.NameValueTable.VALUE, null);
 
+    public static final String RESULT_ROWS_DELETED = "result_rows_deleted";
+
     // Overlay specified settings whitelisted for Instant Apps
     private static final Set<String> OVERLAY_ALLOWED_GLOBAL_INSTANT_APP_SETTINGS = new ArraySet<>();
     private static final Set<String> OVERLAY_ALLOWED_SYSTEM_INSTANT_APP_SETTINGS = new ArraySet<>();
@@ -460,6 +462,27 @@
                 break;
             }
 
+            case Settings.CALL_METHOD_DELETE_SYSTEM: {
+                int rows = deleteSystemSetting(name, requestingUserId) ? 1 : 0;
+                Bundle result = new Bundle();
+                result.putInt(RESULT_ROWS_DELETED, rows);
+                return result;
+            }
+
+            case Settings.CALL_METHOD_DELETE_SECURE: {
+                int rows = deleteSecureSetting(name, requestingUserId, false) ? 1 : 0;
+                Bundle result = new Bundle();
+                result.putInt(RESULT_ROWS_DELETED, rows);
+                return result;
+            }
+
+            case Settings.CALL_METHOD_DELETE_GLOBAL: {
+                int rows = deleteGlobalSetting(name, requestingUserId, false) ? 1 : 0;
+                Bundle result = new Bundle();
+                result.putInt(RESULT_ROWS_DELETED, rows);
+                return result;
+            }
+
             default: {
                 Slog.w(LOG_TAG, "call() with invalid method: " + method);
             } break;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
index 379cfc7..f8445fd 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
@@ -265,8 +265,8 @@
             }
             if (mUser < 0) {
                 mUser = UserHandle.USER_SYSTEM;
-            } else if (mVerb == CommandVerb.DELETE || mVerb == CommandVerb.LIST) {
-                perr.println("--user not supported for delete and list.");
+            } else if (mVerb == CommandVerb.LIST) {
+                perr.println("--user not supported for list.");
                 return -1;
             }
             UserManager userManager = UserManager.get(mProvider.getContext());
@@ -392,22 +392,27 @@
 
         int deleteForUser(IContentProvider provider, int userHandle,
                 final String table, final String key) {
-            Uri targetUri;
-            if ("system".equals(table)) targetUri = Settings.System.getUriFor(key);
-            else if ("secure".equals(table)) targetUri = Settings.Secure.getUriFor(key);
-            else if ("global".equals(table)) targetUri = Settings.Global.getUriFor(key);
-            else {
+            final String callDeleteCommand;
+            if ("system".equals(table)) {
+                callDeleteCommand = Settings.CALL_METHOD_DELETE_SYSTEM;
+            } else if ("secure".equals(table)) {
+                callDeleteCommand = Settings.CALL_METHOD_DELETE_SECURE;
+            } else if ("global".equals(table)) {
+                callDeleteCommand = Settings.CALL_METHOD_DELETE_GLOBAL;
+            } else {
                 getErrPrintWriter().println("Invalid table; no delete performed");
                 throw new IllegalArgumentException("Invalid table " + table);
             }
 
-            int num = 0;
             try {
-                num = provider.delete(resolveCallingPackage(), targetUri, null, null);
+                Bundle arg = new Bundle();
+                arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
+                Bundle result =
+                        provider.call(resolveCallingPackage(), callDeleteCommand, key, arg);
+                return result.getInt(SettingsProvider.RESULT_ROWS_DELETED);
             } catch (RemoteException e) {
                 throw new RuntimeException("Failed in IPC", e);
             }
-            return num;
         }
 
         void resetForUser(IContentProvider provider, int userHandle,
@@ -473,7 +478,7 @@
                 pw.println("      Change the contents of KEY to VALUE.");
                 pw.println("      TAG to associate with the setting.");
                 pw.println("      {default} to set as the default, case-insensitive only for global/secure namespace");
-                pw.println("  delete NAMESPACE KEY");
+                pw.println("  delete [--user <USER_ID> | current] NAMESPACE KEY");
                 pw.println("      Delete the entry for KEY.");
                 pw.println("  reset [--user <USER_ID> | current] NAMESPACE {PACKAGE_NAME | RESET_MODE}");
                 pw.println("      Reset the global/secure table for a package with mode.");
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
index 572a924..183f599 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
@@ -32,9 +32,11 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
-import java.util.concurrent.atomic.AtomicBoolean;
+
 import org.junit.Test;
 
+import java.util.concurrent.atomic.AtomicBoolean;
+
 /**
  * Tests for the SettingContentProvider.
  *
@@ -258,7 +260,7 @@
                     FAKE_SETTING_VALUE, false);
 
             // Reset the changes made by the "shell/root" package
-            resetToDefaultsViaShell(type, "shell");
+            resetToDefaultsViaShell(type, "com.android.shell");
             resetToDefaultsViaShell(type, "root");
 
             // Make sure the old APIs don't set defaults
@@ -272,7 +274,7 @@
                     FAKE_SETTING_VALUE_2, false);
 
             // Reset the changes made by this package
-            resetToDefaultsViaShell(type, "shell");
+            resetToDefaultsViaShell(type, "com.android.shell");
             resetToDefaultsViaShell(type, "root");
 
             // Make sure the old APIs don't set defaults
@@ -313,7 +315,7 @@
                     FAKE_SETTING_VALUE_2, "TOKEN2", false);
 
             // Reset settings associated with TOKEN1
-            resetToDefaultsViaShell(type, "shell", "TOKEN1");
+            resetToDefaultsViaShell(type, "com.android.shell", "TOKEN1");
             resetToDefaultsViaShell(type, "root", "TOKEN1");
 
             // Make sure TOKEN1 settings are reset
@@ -325,7 +327,7 @@
                     FAKE_SETTING_NAME_1));
 
             // Reset settings associated with TOKEN2
-            resetToDefaultsViaShell(type, "shell", "TOKEN2");
+            resetToDefaultsViaShell(type, "com.android.shell", "TOKEN2");
             resetToDefaultsViaShell(type, "root", "TOKEN2");
 
             // Make sure TOKEN2 settings are reset
diff --git a/packages/Shell/OWNERS b/packages/Shell/OWNERS
index 75c0391..6ba1fcb 100644
--- a/packages/Shell/OWNERS
+++ b/packages/Shell/OWNERS
@@ -1,10 +1,12 @@
+set noparent
+
+jsharkey@android.com
+felipeal@google.com
+nandana@google.com
 svetoslavganov@google.com
 hackbod@google.com
 yamasani@google.com
 moltmann@google.com
 toddke@google.com
-jsharkey@google.com
 cbrubaker@google.com
 omakoto@google.com
-nandana@google.com
-felipeal@google.com
diff --git a/packages/SystemUI/res/layout/smart_action_button.xml b/packages/SystemUI/res/layout/smart_action_button.xml
new file mode 100644
index 0000000..2716034
--- /dev/null
+++ b/packages/SystemUI/res/layout/smart_action_button.xml
@@ -0,0 +1,34 @@
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<!-- android:paddingHorizontal is set dynamically in SmartReplyView. -->
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+        style="@android:style/Widget.Material.Button"
+        android:stateListAnimator="@null"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="0dp"
+        android:minHeight="@dimen/smart_reply_button_min_height"
+        android:paddingVertical="@dimen/smart_reply_button_padding_vertical"
+        android:background="@drawable/smart_reply_button_background"
+        android:gravity="center"
+        android:fontFamily="roboto-medium"
+        android:textSize="@dimen/smart_reply_button_font_size"
+        android:lineSpacingExtra="@dimen/smart_reply_button_line_spacing_extra"
+        android:textColor="@color/smart_reply_button_text"
+        android:drawablePadding="@dimen/smart_action_button_icon_padding"
+        android:textStyle="normal"
+        android:ellipsize="none"/>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 07628c6..0997c5b1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -881,6 +881,7 @@
     <dimen name="smart_reply_button_stroke_width">1dp</dimen>
     <dimen name="smart_reply_button_font_size">14sp</dimen>
     <dimen name="smart_reply_button_line_spacing_extra">6sp</dimen> <!-- Total line height 20sp. -->
+    <dimen name="smart_action_button_icon_padding">10dp</dimen>
 
     <!-- A reasonable upper bound for the height of the smart reply button. The measuring code
             needs to start with a guess for the maximum size. Currently two-line smart reply buttons
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index c7910f9..46ed715b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -54,7 +54,6 @@
 import android.util.Log;
 import android.view.IRecentsAnimationController;
 import android.view.IRecentsAnimationRunner;
-
 import android.view.RemoteAnimationTarget;
 
 import com.android.internal.app.IVoiceInteractionManagerService;
@@ -480,4 +479,16 @@
             return false;
         }
     }
+
+    /**
+     * Returns true if the system supports freeform multi-window.
+     */
+    public boolean supportsFreeformMultiWindow(Context context) {
+        final boolean freeformDevOption = Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
+        return ActivityTaskManager.supportsMultiWindow(context)
+                && (context.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
+                || freeformDevOption);
+    }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index 7154f53..a6b66e7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 
 import android.app.ActivityOptions;
@@ -41,6 +42,15 @@
         return options;
     }
 
+    /**
+     * @return ActivityOptions for starting a task in freeform.
+     */
+    public static ActivityOptions makeFreeformOptions() {
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+        return options;
+    }
+
     public static ActivityOptions makeRemoteAnimation(
             RemoteAnimationAdapterCompat remoteAnimationAdapter) {
         return ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter.getWrapped());
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 35ae899..5d33ffd 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -90,7 +90,7 @@
     private float[] mRecentTemps = new float[MAX_RECENT_TEMPS];
     private int mNumTemps;
     private long mNextLogTime;
-    private IThermalService mThermalService;
+    @VisibleForTesting IThermalService mThermalService;
 
     @VisibleForTesting int mBatteryLevel = 100;
     @VisibleForTesting int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
@@ -394,7 +394,7 @@
             // Enable push notifications of throttling from vendor thermal
             // management subsystem via thermalservice, in addition to our
             // usual polling, to react to temperature jumps more quickly.
-            IBinder b = ServiceManager.getService("thermalservice");
+            IBinder b = ServiceManager.getService(Context.THERMAL_SERVICE);
 
             if (b != null) {
                 mThermalService = IThermalService.Stub.asInterface(b);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index fa3fa5b..bb9a341 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -55,6 +55,8 @@
 import com.android.systemui.statusbar.policy.SmartReplyConstants;
 import com.android.systemui.statusbar.policy.SmartReplyView;
 
+import java.util.List;
+
 /**
  * A frame layout containing the actual payload of the notification, including the contracted,
  * expanded and heads up layout. This class is responsible for clipping the content and and
@@ -1285,38 +1287,88 @@
             return;
         }
 
-        Notification notification = entry.notification.getNotification();
+        SmartRepliesAndActions smartRepliesAndActions = chooseSmartRepliesAndActions(
+                mSmartReplyConstants, entry);
 
-        Pair<RemoteInput, Notification.Action> remoteInputActionPair =
-                entry.notification.getNotification().findRemoteInputActionPair(false /*freeform */);
-        Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair =
-                notification.findRemoteInputActionPair(true /*freeform */);
+        applyRemoteInput(entry, smartRepliesAndActions.freeformRemoteInputActionPair != null);
+        applySmartReplyView(smartRepliesAndActions, entry);
+    }
 
-        boolean enableAppGeneratedSmartReplies = (mSmartReplyConstants.isEnabled()
-                && (!mSmartReplyConstants.requiresTargetingP()
+    /**
+     * Chose what smart replies and smart actions to display. App generated suggestions take
+     * precedence. So if the app provides any smart replies, we don't show any
+     * replies or actions generated by the NotificationAssistantService (NAS), and if the app
+     * provides any smart actions we also don't show any NAS-generated replies or actions.
+     */
+    @VisibleForTesting
+    static SmartRepliesAndActions chooseSmartRepliesAndActions(
+            SmartReplyConstants smartReplyConstants,
+            final NotificationData.Entry entry) {
+        boolean enableAppGeneratedSmartReplies = (smartReplyConstants.isEnabled()
+                && (!smartReplyConstants.requiresTargetingP()
                 || entry.targetSdk >= Build.VERSION_CODES.P));
 
-        RemoteInput remoteInputWithChoices = null;
-        PendingIntent pendingIntentWithChoices= null;
-        CharSequence[] choices = null;
-        if (enableAppGeneratedSmartReplies
-                && remoteInputActionPair != null
-                && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices())) {
-            // app generated smart replies
-            remoteInputWithChoices = remoteInputActionPair.first;
-            pendingIntentWithChoices = remoteInputActionPair.second.actionIntent;
-            choices = remoteInputActionPair.first.getChoices();
+        Notification notification = entry.notification.getNotification();
+        Pair<RemoteInput, Notification.Action> remoteInputActionPair =
+                notification.findRemoteInputActionPair(false /* freeform */);
+        Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair =
+                notification.findRemoteInputActionPair(true /* freeform */);
+
+        boolean appGeneratedSmartRepliesExist =
+                enableAppGeneratedSmartReplies
+                        && remoteInputActionPair != null
+                        && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices());
+
+        List<Notification.Action> appGeneratedSmartActions = notification.getContextualActions();
+        boolean appGeneratedSmartActionsExist = !appGeneratedSmartActions.isEmpty();
+
+        if (appGeneratedSmartRepliesExist) {
+            return new SmartRepliesAndActions(remoteInputActionPair.first,
+                    remoteInputActionPair.second.actionIntent,
+                    remoteInputActionPair.first.getChoices(),
+                    appGeneratedSmartActions,
+                    freeformRemoteInputActionPair);
+        } else if (appGeneratedSmartActionsExist) {
+            return new SmartRepliesAndActions(null, null, null, appGeneratedSmartActions,
+                    freeformRemoteInputActionPair);
         } else if (!ArrayUtils.isEmpty(entry.smartReplies)
                 && freeformRemoteInputActionPair != null
                 && freeformRemoteInputActionPair.second.getAllowGeneratedReplies()) {
-            // system generated smart replies
-            remoteInputWithChoices = freeformRemoteInputActionPair.first;
-            pendingIntentWithChoices = freeformRemoteInputActionPair.second.actionIntent;
-            choices = entry.smartReplies;
+            // App didn't generate anything, use NAS-generated replies and actions
+            return new SmartRepliesAndActions(freeformRemoteInputActionPair.first,
+                    freeformRemoteInputActionPair.second.actionIntent,
+                    entry.smartReplies,
+                    entry.systemGeneratedSmartActions,
+                    freeformRemoteInputActionPair);
+        }
+        // App didn't generate anything, and there are no NAS-generated smart replies.
+        return new SmartRepliesAndActions(null, null, null, entry.systemGeneratedSmartActions,
+                freeformRemoteInputActionPair);
+    }
+
+    @VisibleForTesting
+    static class SmartRepliesAndActions {
+        public final RemoteInput remoteInputWithChoices;
+        public final PendingIntent pendingIntentForSmartReplies;
+        public final CharSequence[] smartReplies;
+        public final List<Notification.Action> smartActions;
+        public final Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair;
+
+        SmartRepliesAndActions(RemoteInput remoteInput, PendingIntent pendingIntent,
+                CharSequence[] choices, List<Notification.Action> smartActions,
+                Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair) {
+            this.remoteInputWithChoices = remoteInput;
+            this.pendingIntentForSmartReplies = pendingIntent;
+            this.smartReplies = choices;
+            this.smartActions = smartActions;
+            this.freeformRemoteInputActionPair = freeformRemoteInputActionPair;
         }
 
-        applyRemoteInput(entry, freeformRemoteInputActionPair != null);
-        applySmartReplyView(remoteInputWithChoices, pendingIntentWithChoices, entry, choices);
+        boolean smartRepliesExist() {
+            return remoteInputWithChoices != null
+                    && pendingIntentForSmartReplies != null
+                    && !ArrayUtils.isEmpty(smartReplies);
+        }
     }
 
     private void applyRemoteInput(NotificationData.Entry entry, boolean hasFreeformRemoteInput) {
@@ -1418,28 +1470,32 @@
         return null;
     }
 
-    private void applySmartReplyView(RemoteInput remoteInput, PendingIntent pendingIntent,
-            NotificationData.Entry entry, CharSequence[] choices) {
+    private void applySmartReplyView(SmartRepliesAndActions smartRepliesAndActions,
+            NotificationData.Entry entry) {
         if (mExpandedChild != null) {
             mExpandedSmartReplyView =
-                    applySmartReplyView(mExpandedChild, remoteInput, pendingIntent, entry, choices);
-            if (mExpandedSmartReplyView != null && remoteInput != null
-                    && choices != null && choices.length > 0) {
-                mSmartReplyController.smartRepliesAdded(entry, choices.length);
+                    applySmartReplyView(mExpandedChild, smartRepliesAndActions, entry);
+            if (mExpandedSmartReplyView != null
+                    && smartRepliesAndActions.remoteInputWithChoices != null
+                    && smartRepliesAndActions.smartReplies != null
+                    && smartRepliesAndActions.smartReplies.length > 0) {
+                mSmartReplyController.smartRepliesAdded(entry,
+                        smartRepliesAndActions.smartReplies.length);
             }
         }
     }
 
-    private SmartReplyView applySmartReplyView(
-            View view, RemoteInput remoteInput, PendingIntent pendingIntent,
-            NotificationData.Entry entry, CharSequence[] choices) {
+    private SmartReplyView applySmartReplyView(View view,
+            SmartRepliesAndActions smartRepliesAndActions, NotificationData.Entry entry) {
         View smartReplyContainerCandidate = view.findViewById(
                 com.android.internal.R.id.smart_reply_container);
         if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
             return null;
         }
         LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate;
-        if (remoteInput == null || pendingIntent == null) {
+        // If there are no smart replies and no smart actions - early out.
+        if (!smartRepliesAndActions.smartRepliesExist()
+                && smartRepliesAndActions.smartActions.isEmpty()) {
             smartReplyContainer.setVisibility(View.GONE);
             return null;
         }
@@ -1468,9 +1524,11 @@
             }
         }
         if (smartReplyView != null) {
-            smartReplyView.setRepliesFromRemoteInput(remoteInput, pendingIntent,
-                    mSmartReplyController, entry, smartReplyContainer, choices
-            );
+            smartReplyView.resetSmartSuggestions(smartReplyContainer);
+            smartReplyView.addRepliesFromRemoteInput(smartRepliesAndActions.remoteInputWithChoices,
+                    smartRepliesAndActions.pendingIntentForSmartReplies, mSmartReplyController,
+                    entry, smartRepliesAndActions.smartReplies);
+            smartReplyView.addSmartActions(smartRepliesAndActions.smartActions);
             smartReplyContainer.setVisibility(View.VISIBLE);
         }
         return smartReplyView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index c16b28f..b6ff6fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -367,6 +367,9 @@
     public void onSnapOpen() {
         mMenuSnapped = true;
         mMenuSnappedOnLeft = isMenuOnLeft();
+        if (mAlpha == 0f && mParent != null) {
+            fadeInMenu(mParent.getWidth());
+        }
         if (mMenuListener != null) {
             mMenuListener.onMenuShown(getParent());
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 42f1378..0186683 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -1,6 +1,7 @@
 package com.android.systemui.statusbar.policy;
 
 import android.annotation.ColorInt;
+import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
 import android.content.Context;
@@ -19,6 +20,7 @@
 import android.text.method.TransformationMethod;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.Size;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -30,6 +32,7 @@
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.notification.NotificationData;
@@ -38,14 +41,15 @@
 
 import java.text.BreakIterator;
 import java.util.Comparator;
+import java.util.List;
 import java.util.PriorityQueue;
 
-/** View which displays smart reply buttons in notifications. */
+/** View which displays smart reply and smart actions buttons in notifications. */
 public class SmartReplyView extends ViewGroup {
 
     private static final String TAG = "SmartReplyView";
 
-    private static final int MEASURE_SPEC_ANY_WIDTH =
+    private static final int MEASURE_SPEC_ANY_LENGTH =
             MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
 
     private static final Comparator<View> DECREASING_MEASURED_WIDTH_WITHOUT_PADDING_COMPARATOR =
@@ -98,6 +102,8 @@
     private final int mStrokeWidth;
     private final double mMinStrokeContrast;
 
+    private ActivityStarter mActivityStarter;
+
     public SmartReplyView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mConstants = Dependency.get(SmartReplyConstants.class);
@@ -168,13 +174,24 @@
                 Math.max(getChildCount(), 1), DECREASING_MEASURED_WIDTH_WITHOUT_PADDING_COMPARATOR);
     }
 
-    public void setRepliesFromRemoteInput(
-            RemoteInput remoteInput, PendingIntent pendingIntent,
-            SmartReplyController smartReplyController, NotificationData.Entry entry,
-            View smartReplyContainer, CharSequence[] choices) {
-        mSmartReplyContainer = smartReplyContainer;
+    /**
+     * Reset the smart suggestions view to allow adding new replies and actions.
+     */
+    public void resetSmartSuggestions(View newSmartReplyContainer) {
+        mSmartReplyContainer = newSmartReplyContainer;
         removeAllViews();
         mCurrentBackgroundColor = mDefaultBackgroundColor;
+    }
+
+    /**
+     * Add smart replies to this view, using the provided {@link RemoteInput} and
+     * {@link PendingIntent} to respond when the user taps a smart reply. Only the replies that fit
+     * into the notification are shown.
+     */
+    public void addRepliesFromRemoteInput(
+            RemoteInput remoteInput, PendingIntent pendingIntent,
+            SmartReplyController smartReplyController, NotificationData.Entry entry,
+            CharSequence[] choices) {
         if (remoteInput != null && pendingIntent != null) {
             if (choices != null) {
                 for (int i = 0; i < choices.length; ++i) {
@@ -188,6 +205,22 @@
         reallocateCandidateButtonQueueForSqueezing();
     }
 
+    /**
+     * Add smart actions to be shown next to smart replies. Only the actions that fit into the
+     * notification are shown.
+     */
+    public void addSmartActions(List<Notification.Action> smartActions) {
+        int numSmartActions = smartActions.size();
+        for (int n = 0; n < numSmartActions; n++) {
+            Notification.Action action = smartActions.get(n);
+            if (action.actionIntent != null) {
+                Button actionButton = inflateActionButton(getContext(), this, action);
+                addView(actionButton);
+            }
+        }
+        reallocateCandidateButtonQueueForSqueezing();
+    }
+
     public static SmartReplyView inflate(Context context, ViewGroup root) {
         return (SmartReplyView)
                 LayoutInflater.from(context).inflate(R.layout.smart_reply_view, root, false);
@@ -234,6 +267,48 @@
         return b;
     }
 
+    @VisibleForTesting
+    Button inflateActionButton(Context context, ViewGroup root, Notification.Action action) {
+        Button button = (Button) LayoutInflater.from(context).inflate(
+                R.layout.smart_action_button, root, false);
+        button.setText(action.title);
+
+        Drawable iconDrawable = action.getIcon().loadDrawable(context);
+        // Add the action icon to the Smart Action button.
+        Size newIconSize = calculateIconSizeFromSingleLineButton(context, root,
+                new Size(iconDrawable.getIntrinsicWidth(), iconDrawable.getIntrinsicHeight()));
+        iconDrawable.setBounds(0, 0, newIconSize.getWidth(), newIconSize.getHeight());
+        button.setCompoundDrawables(iconDrawable, null, null, null);
+
+        button.setOnClickListener(view ->
+                getActivityStarter().startPendingIntentDismissingKeyguard(action.actionIntent));
+
+        // TODO(b/119010281): handle accessibility
+
+        return button;
+    }
+
+    private static Size calculateIconSizeFromSingleLineButton(Context context, ViewGroup root,
+            Size originalIconSize) {
+        Button button = (Button) LayoutInflater.from(context).inflate(
+                R.layout.smart_action_button, root, false);
+        // Add simple text here to ensure the button displays one line of text.
+        button.setText("a");
+        return calculateIconSizeFromButtonHeight(button, originalIconSize);
+    }
+
+    // Given a button with text on a single line - we want to add an icon to that button. This
+    // method calculates the icon height to use to avoid making the button grow in height.
+    private static Size calculateIconSizeFromButtonHeight(Button button, Size originalIconSize) {
+        // A completely permissive measure spec should make the button text single-line.
+        button.measure(MEASURE_SPEC_ANY_LENGTH, MEASURE_SPEC_ANY_LENGTH);
+        int buttonHeight = button.getMeasuredHeight();
+        int newIconHeight = buttonHeight / 2;
+        int newIconWidth = (int) (originalIconSize.getWidth()
+                * ((double) newIconHeight) / originalIconSize.getHeight());
+        return new Size(newIconWidth, newIconHeight);
+    }
+
     @Override
     public LayoutParams generateLayoutParams(AttributeSet attrs) {
         return new LayoutParams(mContext, attrs);
@@ -277,7 +352,7 @@
 
             child.setPadding(buttonPaddingHorizontal, child.getPaddingTop(),
                     buttonPaddingHorizontal, child.getPaddingBottom());
-            child.measure(MEASURE_SPEC_ANY_WIDTH, heightMeasureSpec);
+            child.measure(MEASURE_SPEC_ANY_LENGTH, heightMeasureSpec);
 
             final int lineCount = ((Button) child).getLineCount();
             if (lineCount < 1 || lineCount > 2) {
@@ -437,6 +512,18 @@
         return (int) Math.ceil(optimalTextWidth);
     }
 
+    /**
+     * Returns the combined width of the left drawable (the action icon) and the padding between the
+     * drawable and the button text.
+     */
+    private int getLeftCompoundDrawableWidthWithPadding(Button button) {
+        Drawable[] drawables = button.getCompoundDrawables();
+        Drawable leftDrawable = drawables[0];
+        if (leftDrawable == null) return 0;
+
+        return leftDrawable.getBounds().width() + button.getCompoundDrawablePadding();
+    }
+
     private int squeezeButtonToTextWidth(Button button, int heightMeasureSpec, int textWidth) {
         int oldWidth = button.getMeasuredWidth();
         if (button.getPaddingLeft() != mDoubleLineButtonPaddingHorizontal) {
@@ -449,7 +536,8 @@
         button.setPadding(mDoubleLineButtonPaddingHorizontal, button.getPaddingTop(),
                 mDoubleLineButtonPaddingHorizontal, button.getPaddingBottom());
         final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(
-                2 * mDoubleLineButtonPaddingHorizontal + textWidth, MeasureSpec.AT_MOST);
+                2 * mDoubleLineButtonPaddingHorizontal + textWidth
+                      + getLeftCompoundDrawableWidthWithPadding(button), MeasureSpec.AT_MOST);
         button.measure(widthMeasureSpec, heightMeasureSpec);
 
         final int newWidth = button.getMeasuredWidth();
@@ -607,6 +695,13 @@
         button.setTextColor(textColor);
     }
 
+    private ActivityStarter getActivityStarter() {
+        if (mActivityStarter == null) {
+            mActivityStarter = Dependency.get(ActivityStarter.class);
+        }
+        return mActivityStarter;
+    }
+
     @VisibleForTesting
     static class LayoutParams extends ViewGroup.LayoutParams {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index b44630a..221cbe9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -32,12 +32,13 @@
 import android.content.Intent;
 import android.os.BatteryManager;
 import android.os.HardwarePropertiesManager;
+import android.os.IThermalService;
 import android.os.PowerManager;
 import android.provider.Settings;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 import android.testing.TestableResources;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.R;
@@ -45,15 +46,16 @@
 import com.android.systemui.power.PowerUI.WarningsUI;
 import com.android.systemui.statusbar.phone.StatusBar;
 
-import java.time.Duration;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.time.Duration;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
 @SmallTest
@@ -76,6 +78,7 @@
     private PowerUI mPowerUI;
     private EnhancedEstimates mEnhancedEstimates;
     @Mock private PowerManager mPowerManager;
+    @Mock private IThermalService mThermalServiceMock;
 
     @Before
     public void setup() {
@@ -541,5 +544,6 @@
         mPowerUI = new PowerUI();
         mPowerUI.mContext = mContext;
         mPowerUI.mComponents = mContext.getComponents();
+        mPowerUI.mThermalService = mThermalServiceMock;
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
index f59bfae..b3b45eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -39,6 +39,7 @@
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.PendingIntent;
+import android.app.Person;
 import android.content.Intent;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
@@ -421,6 +422,33 @@
         assertEquals(snoozeCriterions, entry.snoozeCriteria);
     }
 
+    @Test
+    public void notificationDataEntry_testIsLastMessageFromReply() {
+        Person.Builder person = new Person.Builder()
+                .setName("name")
+                .setKey("abc")
+                .setUri("uri")
+                .setBot(true);
+
+        // EXTRA_MESSAGING_PERSON is the same Person as the sender in last message in EXTRA_MESSAGES
+        Bundle bundle = new Bundle();
+        bundle.putParcelable(Notification.EXTRA_MESSAGING_PERSON, person.build());
+        Bundle[] messagesBundle = new Bundle[]{ new Notification.MessagingStyle.Message(
+                "text", 0, person.build()).toBundle() };
+        bundle.putParcelableArray(Notification.EXTRA_MESSAGES, messagesBundle);
+
+        Notification notification = new Notification.Builder(mContext, "test")
+                .addExtras(bundle)
+                .build();
+        StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
+                notification, mContext.getUser(), "", 0);
+
+        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        entry.setHasSentReply();
+
+        assertTrue(entry.isLastMessageFromReply());
+    }
+
     private void initStatusBarNotification(boolean allowDuringSetup) {
         Bundle bundle = new Bundle();
         bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index c189c95..a6725b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -16,6 +16,11 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.Mockito.doNothing;
@@ -28,29 +33,62 @@
 import static org.mockito.Mockito.when;
 
 import android.app.AppOpsManager;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.service.notification.StatusBarNotification;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.ArraySet;
+import android.util.Pair;
 import android.view.NotificationHeaderView;
 import android.view.View;
 
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.policy.SmartReplyConstants;
 
-import org.junit.Assert;
 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;
+import java.util.List;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class NotificationContentViewTest extends SysuiTestCase {
 
+    private static final String TEST_ACTION = "com.android.SMART_REPLY_VIEW_ACTION";
+
     NotificationContentView mView;
 
+    @Mock
+    SmartReplyConstants mSmartReplyConstants;
+    @Mock
+    StatusBarNotification mStatusBarNotification;
+    @Mock
+    Notification mNotification;
+    NotificationData.Entry mEntry;
+    @Mock
+    RemoteInput mRemoteInput;
+    @Mock
+    RemoteInput mFreeFormRemoteInput;
+
+    private Icon mActionIcon;
+
+
     @Before
     @UiThreadTest
     public void setup() {
+        MockitoAnnotations.initMocks(this);
+
         mView = new NotificationContentView(mContext, null);
         ExpandableNotificationRow row = new ExpandableNotificationRow(mContext, null);
         ExpandableNotificationRow mockRow = spy(row);
@@ -67,6 +105,12 @@
 
         mView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
         mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+
+        // Smart replies
+        when(mStatusBarNotification.getNotification()).thenReturn(mNotification);
+        mEntry = new NotificationData.Entry(mStatusBarNotification);
+        when(mSmartReplyConstants.isEnabled()).thenReturn(true);
+        mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
     }
 
     private View createViewWithHeight(int height) {
@@ -82,7 +126,7 @@
         mView.setDark(true, false, 0);
         mView.setDark(false, true, 0);
         mView.setHeadsUpAnimatingAway(true);
-        Assert.assertFalse(mView.isAnimatingVisibleType());
+        assertFalse(mView.isAnimatingVisibleType());
     }
 
     @Test
@@ -115,4 +159,161 @@
         verify(mockAmbient, never()).showAppOpsIcons(ops);
         verify(mockHeadsUp, times(1)).showAppOpsIcons(any());
     }
+
+    private void setupAppGeneratedReplies(CharSequence[] smartReplyTitles) {
+        Notification.Action freeFormAction =
+                new Notification.Action.Builder(null, "Freeform Test Action", null).build();
+        setupAppGeneratedReplies(smartReplyTitles, freeFormAction);
+    }
+
+    private void setupAppGeneratedReplies(
+            CharSequence[] smartReplyTitles,
+            Notification.Action freeFormRemoteInputAction) {
+        Notification.Action action =
+                new Notification.Action.Builder(null, "Test Action", null).build();
+        when(mRemoteInput.getChoices()).thenReturn(smartReplyTitles);
+        Pair<RemoteInput, Notification.Action> remoteInputActionPair =
+                Pair.create(mRemoteInput, action);
+        when(mNotification.findRemoteInputActionPair(false)).thenReturn(remoteInputActionPair);
+
+        Pair<RemoteInput, Notification.Action> freeFormRemoteInputActionPair =
+                Pair.create(mFreeFormRemoteInput, freeFormRemoteInputAction);
+        when(mNotification.findRemoteInputActionPair(true)).thenReturn(
+                freeFormRemoteInputActionPair);
+    }
+
+    @Test
+    public void chooseSmartRepliesAndActions_smartRepliesOff_noAppGeneratedSmartReplies() {
+        setupAppGeneratedReplies(new String[] {"Reply1", "Reply2"});
+        when(mSmartReplyConstants.isEnabled()).thenReturn(false);
+
+        NotificationContentView.SmartRepliesAndActions repliesAndActions =
+                NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+        assertFalse(repliesAndActions.smartRepliesExist());
+    }
+
+    @Test
+    public void chooseSmartRepliesAndActions_appGeneratedSmartReplies() {
+        CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"};
+        setupAppGeneratedReplies(smartReplies);
+        when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
+
+        NotificationContentView.SmartRepliesAndActions repliesAndActions =
+                NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+        assertThat(repliesAndActions.smartReplies, equalTo(smartReplies));
+    }
+
+    @Test
+    public void chooseSmartRepliesAndActions_appGeneratedSmartRepliesAndActions() {
+        CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"};
+        setupAppGeneratedReplies(smartReplies);
+        when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
+
+        List<Notification.Action> smartActions =
+                createActions(new String[] {"Test Action 1", "Test Action 2"});
+        when(mNotification.getContextualActions()).thenReturn(smartActions);
+
+        NotificationContentView.SmartRepliesAndActions repliesAndActions =
+                NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+        assertThat(repliesAndActions.smartReplies, equalTo(smartReplies));
+        assertThat(repliesAndActions.smartActions, equalTo(smartActions));
+    }
+
+    @Test
+    public void chooseSmartRepliesAndActions_sysGeneratedSmartReplies() {
+        Notification.Action freeFormAction = createActionBuilder("Freeform Action")
+                .setAllowGeneratedReplies(true)
+                .build();
+        // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+        // replies.
+        setupAppGeneratedReplies(null, freeFormAction);
+
+        mEntry.smartReplies =
+                new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+        NotificationContentView.SmartRepliesAndActions repliesAndActions =
+                NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+        assertThat(repliesAndActions.smartReplies, equalTo(mEntry.smartReplies));
+        assertThat(repliesAndActions.smartActions, is(empty()));
+    }
+
+    @Test
+    public void chooseSmartRepliesAndActions_noSysGeneratedSmartRepliesIfNotAllowed() {
+        Notification.Action freeFormAction = createActionBuilder("Freeform Action")
+                .setAllowGeneratedReplies(false)
+                .build();
+        // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+        // replies.
+        setupAppGeneratedReplies(null, freeFormAction);
+
+        mEntry.smartReplies =
+                new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+        NotificationContentView.SmartRepliesAndActions repliesAndActions =
+                NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+        assertThat(repliesAndActions.smartReplies, equalTo(null));
+        assertThat(repliesAndActions.smartActions, is(empty()));
+    }
+
+    @Test
+    public void chooseSmartRepliesAndActions_sysGeneratedSmartActions() {
+        // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+        // actions.
+        setupAppGeneratedReplies(null);
+
+        mEntry.systemGeneratedSmartActions =
+                createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+        NotificationContentView.SmartRepliesAndActions repliesAndActions =
+                NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+        assertThat(repliesAndActions.smartReplies, equalTo(null));
+        assertThat(repliesAndActions.smartActions, equalTo(mEntry.systemGeneratedSmartActions));
+    }
+
+    @Test
+    public void chooseSmartRepliesAndActions_appGenPreferredOverSysGen() {
+        Notification.Action freeFormAction = createActionBuilder("Freeform Action")
+                .setAllowGeneratedReplies(true)
+                .build();
+        CharSequence[] appGenSmartReplies = new String[] {"Reply1", "Reply2"};
+        // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+        // replies.
+        setupAppGeneratedReplies(appGenSmartReplies, freeFormAction);
+        when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
+
+        List<Notification.Action> appGenSmartActions =
+                createActions(new String[] {"Test Action 1", "Test Action 2"});
+        when(mNotification.getContextualActions()).thenReturn(appGenSmartActions);
+
+        mEntry.smartReplies = new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+        mEntry.systemGeneratedSmartActions =
+                createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+
+        NotificationContentView.SmartRepliesAndActions repliesAndActions =
+                NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+        assertThat(repliesAndActions.smartReplies, equalTo(appGenSmartReplies));
+        assertThat(repliesAndActions.smartActions, equalTo(appGenSmartActions));
+    }
+
+    private Notification.Action.Builder createActionBuilder(String actionTitle) {
+        PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
+                new Intent(TEST_ACTION), 0);
+        return new Notification.Action.Builder(mActionIcon, actionTitle, pendingIntent);
+    }
+
+    private Notification.Action createAction(String actionTitle) {
+        return createActionBuilder(actionTitle).build();
+    }
+
+    private List<Notification.Action> createActions(String[] actionTitles) {
+        List<Notification.Action> actions = new ArrayList<>();
+        for (String title : actionTitles) {
+            actions.add(createAction(title));
+        }
+        return actions;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 4534ebe..9e659c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -22,7 +22,9 @@
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -32,6 +34,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.service.notification.StatusBarNotification;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -41,14 +45,14 @@
 import android.widget.Button;
 import android.widget.LinearLayout;
 
-import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
-
-import java.util.concurrent.atomic.AtomicReference;
+import com.android.systemui.statusbar.phone.ShadeController;
 
 import org.junit.After;
 import org.junit.Before;
@@ -57,6 +61,10 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 @SmallTest
@@ -67,6 +75,10 @@
     private static final String[] TEST_CHOICES = new String[]{"Hello", "What's up?", "I'm here"};
     private static final String TEST_NOTIFICATION_KEY = "akey";
 
+    private static final String[] TEST_ACTION_TITLES = new String[]{
+            "First action", "Open something", "Action"
+    };
+
     private static final int WIDTH_SPEC = MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY);
     private static final int HEIGHT_SPEC = MeasureSpec.makeMeasureSpec(400, MeasureSpec.AT_MOST);
 
@@ -74,6 +86,8 @@
     private SmartReplyView mView;
     private View mContainer;
 
+    private Icon mActionIcon;
+
     private int mSingleLinePaddingHorizontal;
     private int mDoubleLinePaddingHorizontal;
     private int mSpacing;
@@ -82,12 +96,16 @@
     private NotificationData.Entry mEntry;
     private Notification mNotification;
 
+    @Mock ActivityStarter mActivityStarter;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mReceiver = new BlockingQueueIntentReceiver();
         mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION));
         mDependency.get(KeyguardDismissUtil.class).setDismissHandler(action -> action.onDismiss());
+        mDependency.injectMockDependency(ShadeController.class);
+        mDependency.injectTestDependency(ActivityStarter.class, mActivityStarter);
 
         mContainer = new View(mContext, null);
         mView = SmartReplyView.inflate(mContext, null);
@@ -108,6 +126,8 @@
         when(sbn.getNotification()).thenReturn(mNotification);
         when(sbn.getKey()).thenReturn(TEST_NOTIFICATION_KEY);
         mEntry = new NotificationData.Entry(sbn);
+
+        mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
     }
 
     @After
@@ -117,7 +137,7 @@
 
     @Test
     public void testSendSmartReply_intentContainsResultsAndSource() throws InterruptedException {
-        setRepliesFromRemoteInput(TEST_CHOICES);
+        setSmartReplies(TEST_CHOICES);
 
         mView.getChildAt(2).performClick();
 
@@ -130,7 +150,7 @@
     @Test
     public void testSendSmartReply_keyguardCancelled() throws InterruptedException {
         mDependency.get(KeyguardDismissUtil.class).setDismissHandler(action -> {});
-        setRepliesFromRemoteInput(TEST_CHOICES);
+        setSmartReplies(TEST_CHOICES);
 
         mView.getChildAt(2).performClick();
 
@@ -141,7 +161,7 @@
     public void testSendSmartReply_waitsForKeyguard() throws InterruptedException {
         AtomicReference<OnDismissAction> actionRef = new AtomicReference<>();
         mDependency.get(KeyguardDismissUtil.class).setDismissHandler(actionRef::set);
-        setRepliesFromRemoteInput(TEST_CHOICES);
+        setSmartReplies(TEST_CHOICES);
 
         mView.getChildAt(2).performClick();
 
@@ -159,7 +179,7 @@
 
     @Test
     public void testSendSmartReply_controllerCalled() {
-        setRepliesFromRemoteInput(TEST_CHOICES);
+        setSmartReplies(TEST_CHOICES);
         mView.getChildAt(2).performClick();
         verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2]);
     }
@@ -167,7 +187,7 @@
     @Test
     public void testSendSmartReply_hidesContainer() {
         mContainer.setVisibility(View.VISIBLE);
-        setRepliesFromRemoteInput(TEST_CHOICES);
+        setSmartReplies(TEST_CHOICES);
         mView.getChildAt(0).performClick();
         assertEquals(View.GONE, mContainer.getVisibility());
     }
@@ -198,7 +218,7 @@
         ViewGroup expectedView = buildExpectedView(choices, 1);
         expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
 
-        setRepliesFromRemoteInput(choices);
+        setSmartReplies(choices);
         mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
 
         assertEqualMeasures(expectedView, mView);
@@ -217,7 +237,7 @@
         expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
                 10 + expectedView.getMeasuredHeight());
 
-        setRepliesFromRemoteInput(choices);
+        setSmartReplies(choices);
         mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
         mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
 
@@ -235,7 +255,7 @@
         ViewGroup expectedView = buildExpectedView(choices, 2);
         expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
 
-        setRepliesFromRemoteInput(choices);
+        setSmartReplies(choices);
         mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
 
         assertEqualMeasures(expectedView, mView);
@@ -254,7 +274,7 @@
         expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
                 10 + expectedView.getMeasuredHeight());
 
-        setRepliesFromRemoteInput(choices);
+        setSmartReplies(choices);
         mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
         mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
 
@@ -273,7 +293,7 @@
         ViewGroup expectedView = buildExpectedView(new CharSequence[]{"Hi", "Bye"}, 1);
         expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
 
-        setRepliesFromRemoteInput(choices);
+        setSmartReplies(choices);
         mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
 
         assertEqualMeasures(expectedView, mView);
@@ -293,7 +313,7 @@
         expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
                 10 + expectedView.getMeasuredHeight());
 
-        setRepliesFromRemoteInput(choices);
+        setSmartReplies(choices);
         mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
         mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
 
@@ -313,7 +333,7 @@
                 new CharSequence[]{"Short", "Short", "Looooooong \nreplyyyyy"}, 2);
         expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
 
-        setRepliesFromRemoteInput(choices);
+        setSmartReplies(choices);
         mView.measure(
                 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
                 MeasureSpec.UNSPECIFIED);
@@ -335,7 +355,7 @@
         expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
                 10 + expectedView.getMeasuredHeight());
 
-        setRepliesFromRemoteInput(choices);
+        setSmartReplies(choices);
         mView.measure(
                 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
                 MeasureSpec.UNSPECIFIED);
@@ -359,7 +379,7 @@
         expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
                 10 + expectedView.getMeasuredHeight());
 
-        setRepliesFromRemoteInput(choices);
+        setSmartReplies(choices);
         mView.measure(
                 MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
                 MeasureSpec.UNSPECIFIED);
@@ -371,15 +391,45 @@
         assertReplyButtonHidden(mView.getChildAt(2));
     }
 
-    private void setRepliesFromRemoteInput(CharSequence[] choices) {
+    private void setSmartReplies(CharSequence[] choices) {
         PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
                 new Intent(TEST_ACTION), 0);
         RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).setChoices(choices).build();
-        mView.setRepliesFromRemoteInput(input, pendingIntent, mLogger, mEntry, mContainer, choices);
+        mView.resetSmartSuggestions(mContainer);
+        mView.addRepliesFromRemoteInput(input, pendingIntent, mLogger, mEntry, choices);
+    }
+
+    private Notification.Action createAction(String actionTitle) {
+        PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
+                new Intent(TEST_ACTION), 0);
+        return new Notification.Action.Builder(mActionIcon, actionTitle, pendingIntent).build();
+    }
+
+    private List<Notification.Action> createActions(String[] actionTitles) {
+        List<Notification.Action> actions = new ArrayList<>();
+        for (String title : actionTitles) {
+            actions.add(createAction(title));
+        }
+        return actions;
+    }
+
+    private void setSmartActions(String[] actionTitles) {
+        mView.resetSmartSuggestions(mContainer);
+        mView.addSmartActions(createActions(actionTitles));
+    }
+
+    private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) {
+        setSmartReplies(choices);
+        mView.addSmartActions(createActions(actionTitles));
+    }
+
+    private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
+        return buildExpectedView(choices, lineCount, new ArrayList<>());
     }
 
     /** Builds a {@link ViewGroup} whose measures and layout mirror a {@link SmartReplyView}. */
-    private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
+    private ViewGroup buildExpectedView(
+            CharSequence[] choices, int lineCount, List<Notification.Action> actions) {
         LinearLayout layout = new LinearLayout(mContext);
         layout.setOrientation(LinearLayout.HORIZONTAL);
 
@@ -401,6 +451,7 @@
                 return null;
         }
 
+        // Add smart replies
         Button previous = null;
         for (int i = 0; i < choices.length; ++i) {
             Button current = mView.inflateReplyButton(mContext, mView, i, choices[i],
@@ -420,6 +471,24 @@
             previous = current;
         }
 
+        // Add smart actions
+        for (int i = 0; i < actions.size(); ++i) {
+            Button current = inflateActionButton(actions.get(i));
+            current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
+                    current.getPaddingBottom());
+            if (previous != null) {
+                ViewGroup.MarginLayoutParams lp =
+                        (ViewGroup.MarginLayoutParams) previous.getLayoutParams();
+                if (isRtl) {
+                    lp.leftMargin = mSpacing;
+                } else {
+                    lp.rightMargin = mSpacing;
+                }
+            }
+            layout.addView(current);
+            previous = current;
+        }
+
         return layout;
     }
 
@@ -455,4 +524,255 @@
         assertEquals(expected.getPaddingRight(), actual.getPaddingRight());
         assertEquals(expected.getPaddingBottom(), actual.getPaddingBottom());
     }
+
+
+    // =============================================================================================
+    // ============================= Smart Action tests ============================================
+    // =============================================================================================
+
+    @Test
+    public void testTapSmartAction_waitsForKeyguard() throws InterruptedException {
+        setSmartActions(TEST_ACTION_TITLES);
+
+        mView.getChildAt(2).performClick();
+
+        verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any());
+    }
+
+    @Test
+    public void testMeasure_shortSmartActions() {
+        String[] actions = new String[] {"Hi", "Hello", "Bye"};
+        // All choices should be displayed as SINGLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1, createActions(actions));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        setSmartActions(actions);
+        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        assertEqualMeasures(expectedView, mView);
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+    }
+
+    @Test
+    public void testLayout_shortSmartActions() {
+        String[] actions = new String[] {"Hi", "Hello", "Bye"};
+        // All choices should be displayed as SINGLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1, createActions(actions));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
+                10 + expectedView.getMeasuredHeight());
+
+        setSmartActions(actions);
+        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
+
+        assertEqualLayouts(expectedView, mView);
+        assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
+        assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
+    }
+
+    @Test
+    public void testMeasure_smartActionWithTwoLines() {
+        String[] actions = new String[] {"Hi", "Hello\neveryone", "Bye"};
+
+        // All actions should be displayed as DOUBLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2, createActions(actions));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        setSmartActions(actions);
+        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        assertEqualMeasures(expectedView, mView);
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+    }
+
+    @Test
+    public void testLayout_smartActionWithTwoLines() {
+        String[] actions = new String[] {"Hi", "Hello\neveryone", "Bye"};
+
+        // All actions should be displayed as DOUBLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2, createActions(actions));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
+                10 + expectedView.getMeasuredHeight());
+
+        setSmartActions(actions);
+        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
+
+        assertEqualLayouts(expectedView, mView);
+        assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
+        assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
+    }
+
+    @Test
+    public void testMeasure_smartActionWithThreeLines() {
+        String[] actions = new String[] {"Hi", "Hello\nevery\nbody", "Bye"};
+
+        // The action with three lines should NOT be displayed. All other actions should be
+        // displayed as SINGLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1,
+                createActions(new String[]{"Hi", "Bye"}));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        setSmartActions(actions);
+        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        assertEqualMeasures(expectedView, mView);
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertReplyButtonHidden(mView.getChildAt(1));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
+    }
+
+    @Test
+    public void testLayout_smartActionWithThreeLines() {
+        String[] actions = new String[] {"Hi", "Hello\nevery\nbody", "Bye"};
+
+        // The action with three lines should NOT be displayed. All other actions should be
+        // displayed as SINGLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1,
+                createActions(new String[]{"Hi", "Bye"}));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
+                10 + expectedView.getMeasuredHeight());
+
+        setSmartActions(actions);
+        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
+
+        assertEqualLayouts(expectedView, mView);
+        assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
+        // We don't care about mView.getChildAt(1)'s layout because it's hidden (see
+        // testMeasure_smartActionWithThreeLines).
+        assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(2));
+    }
+
+    @Test
+    public void testMeasure_squeezeLongestSmartAction() {
+        String[] actions = new String[] {"Short", "Short", "Looooooong replyyyyy"};
+
+        // All actions should be displayed as DOUBLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2,
+                createActions(new String[] {"Short", "Short", "Looooooong \nreplyyyyy"}));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        setSmartActions(actions);
+        mView.measure(
+                MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+                MeasureSpec.UNSPECIFIED);
+
+        assertEqualMeasures(expectedView, mView);
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+    }
+
+    @Test
+    public void testLayout_squeezeLongestSmartAction() {
+        String[] actions = new String[] {"Short", "Short", "Looooooong replyyyyy"};
+
+        // All actions should be displayed as DOUBLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2,
+                createActions(new String[] {"Short", "Short", "Looooooong \nreplyyyyy"}));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
+                10 + expectedView.getMeasuredHeight());
+
+        setSmartActions(actions);
+        mView.measure(
+                MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+                MeasureSpec.UNSPECIFIED);
+        mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
+
+        assertEqualLayouts(expectedView, mView);
+        assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
+        assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
+    }
+
+    @Test
+    public void testMeasure_dropLongestSmartAction() {
+        String[] actions = new String[] {"Short", "Short", "LooooooongUnbreakableReplyyyyy"};
+
+        // Short actions should be shown as single line views
+        ViewGroup expectedView = buildExpectedView(
+                new CharSequence[0], 1, createActions(new String[] {"Short", "Short"}));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
+                10 + expectedView.getMeasuredHeight());
+
+        setSmartActions(actions);
+        mView.measure(
+                MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+                MeasureSpec.UNSPECIFIED);
+        mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
+
+        assertEqualLayouts(expectedView, mView);
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+        assertReplyButtonHidden(mView.getChildAt(2));
+    }
+
+    private Button inflateActionButton(Notification.Action action) {
+        return mView.inflateActionButton(getContext(), mView, action);
+    }
+
+    @Test
+    public void testInflateActionButton_smartActionIconSingleLineSizeForTwoLineButton() {
+        // Ensure smart action icons are the same size regardless of the number of text rows in the
+        // button.
+        Button singleLineButton = inflateActionButton(createAction("One line"));
+        Button doubleLineButton = inflateActionButton(createAction("Two\nlines"));
+        Drawable singleLineDrawable = singleLineButton.getCompoundDrawables()[0]; // left drawable
+        Drawable doubleLineDrawable = doubleLineButton.getCompoundDrawables()[0]; // left drawable
+        assertEquals(singleLineDrawable.getBounds().width(),
+                     doubleLineDrawable.getBounds().width());
+        assertEquals(singleLineDrawable.getBounds().height(),
+                     doubleLineDrawable.getBounds().height());
+    }
+
+    @Test
+    public void testMeasure_shortChoicesAndActions() {
+        CharSequence[] choices = new String[] {"Hi", "Hello"};
+        String[] actions = new String[] {"Bye"};
+        // All choices should be displayed as SINGLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(choices, 1, createActions(actions));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        setSmartRepliesAndActions(choices, actions);
+        mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        assertEqualMeasures(expectedView, mView);
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+    }
+
+    @Test
+    public void testMeasure_choicesAndActionsSqueezeLongestAction() {
+        CharSequence[] choices = new String[] {"Short", "Short"};
+        String[] actions = new String[] {"Looooooong replyyyyy"};
+
+        // All actions should be displayed as DOUBLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(choices, 2,
+                createActions(new String[] {"Looooooong \nreplyyyyy"}));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        setSmartRepliesAndActions(choices, actions);
+        mView.measure(
+                MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+                MeasureSpec.UNSPECIFIED);
+
+        assertEqualMeasures(expectedView, mView);
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 5c189ce..d5decce 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1832,8 +1832,8 @@
         updateMagnificationLocked(userState);
         scheduleUpdateFingerprintGestureHandling(userState);
         scheduleUpdateInputFilter(userState);
-        scheduleUpdateClientsIfNeededLocked(userState);
         updateRelevantEventsLocked(userState);
+        scheduleUpdateClientsIfNeededLocked(userState);
         updateAccessibilityButtonTargetsLocked(userState);
     }
 
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 92d8d73..87a42fa 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1328,40 +1328,12 @@
         modifyInterfaceForward(false, fromIface, toIface);
     }
 
-    private void modifyNat(String action, String internalInterface, String externalInterface)
-            throws SocketException {
-        final Command cmd = new Command("nat", action, internalInterface, externalInterface);
-
-        final NetworkInterface internalNetworkInterface = NetworkInterface.getByName(
-                internalInterface);
-        if (internalNetworkInterface == null) {
-            cmd.appendArg("0");
-        } else {
-            // Don't touch link-local routes, as link-local addresses aren't routable,
-            // kernel creates link-local routes on all interfaces automatically
-            List<InterfaceAddress> interfaceAddresses = excludeLinkLocal(
-                    internalNetworkInterface.getInterfaceAddresses());
-            cmd.appendArg(interfaceAddresses.size());
-            for (InterfaceAddress ia : interfaceAddresses) {
-                InetAddress addr = NetworkUtils.getNetworkPart(
-                        ia.getAddress(), ia.getNetworkPrefixLength());
-                cmd.appendArg(addr.getHostAddress() + "/" + ia.getNetworkPrefixLength());
-            }
-        }
-
-        try {
-            mConnector.execute(cmd);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
-        }
-    }
-
     @Override
     public void enableNat(String internalInterface, String externalInterface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            modifyNat("enable", internalInterface, externalInterface);
-        } catch (SocketException e) {
+            mNetdService.tetherAddForward(internalInterface, externalInterface);
+        } catch (RemoteException | ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
     }
@@ -1370,8 +1342,8 @@
     public void disableNat(String internalInterface, String externalInterface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            modifyNat("disable", internalInterface, externalInterface);
-        } catch (SocketException e) {
+            mNetdService.tetherRemoveForward(internalInterface, externalInterface);
+        } catch (RemoteException | ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
     }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index c660cc6..a19e928 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1702,8 +1702,11 @@
                     s.app.whitelistManager = true;
                 }
                 // This could have made the service more important.
-                mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities()
-                        || s.app.treatLikeActivity, b.client);
+                mAm.updateLruProcessLocked(s.app,
+                        (callerApp.hasActivitiesOrRecentTasks() && s.app.hasClientActivities())
+                                || (callerApp.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP
+                                        && (flags & Context.BIND_TREAT_LIKE_ACTIVITY) != 0),
+                        b.client);
                 mAm.updateOomAdjLocked(s.app, true);
             }
 
@@ -1787,6 +1790,32 @@
         }
     }
 
+    void updateServiceGroupLocked(IServiceConnection connection, int group, int importance) {
+        final IBinder binder = connection.asBinder();
+        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "updateServiceGroup: conn=" + binder);
+        final ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
+        if (clist == null) {
+            throw new IllegalArgumentException("Could not find connection for "
+                    + connection.asBinder());
+        }
+        for (int i = clist.size() - 1; i >= 0; i--) {
+            final ConnectionRecord crec = clist.get(i);
+            final ServiceRecord srec = crec.binding.service;
+            if (srec != null && srec.app != null
+                    && (srec.serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
+                if (group > 0) {
+                    srec.app.connectionService = srec;
+                    srec.app.connectionGroup = group;
+                    srec.app.connectionImportance = importance;
+                } else {
+                    srec.app.connectionService = null;
+                    srec.app.connectionGroup = 0;
+                    srec.app.connectionImportance = 0;
+                }
+            }
+        }
+    }
+
     boolean unbindServiceLocked(IServiceConnection connection) {
         IBinder binder = connection.asBinder();
         if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "unbindService: conn=" + binder);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4e417ba..7e9e83c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9207,26 +9207,33 @@
                 }
                 dumpAssociationsLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
             }
+            if (dumpPackage == null) {
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                mOomAdjProfiler.dump(pw);
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                dumpBinderProxies(pw);
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                dumpLmkLocked(pw);
+            }
+            pw.println();
+            if (dumpAll) {
+                pw.println("-------------------------------------------------------------------------------");
+            }
+            dumpLruLocked(pw, dumpPackage);
             pw.println();
             if (dumpAll) {
                 pw.println("-------------------------------------------------------------------------------");
             }
             dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
-            pw.println();
-            if (dumpAll) {
-                pw.println("-------------------------------------------------------------------------------");
-            }
-            mOomAdjProfiler.dump(pw);
-            pw.println();
-            if (dumpAll) {
-                pw.println("-------------------------------------------------------------------------------");
-            }
-            dumpBinderProxies(pw);
-            pw.println();
-            if (dumpAll) {
-                pw.println("-------------------------------------------------------------------------------");
-            }
-            dumpLmkLocked(pw);
         }
     }
 
@@ -9421,6 +9428,10 @@
                 synchronized (this) {
                     dumpLmkLocked(pw);
                 }
+            } else if ("lru".equals(cmd)) {
+                synchronized (this) {
+                    dumpLruLocked(pw, null);
+                }
             } else if ("permissions".equals(cmd) || "perm".equals(cmd)) {
                 synchronized (this) {
                     dumpPermissionsLocked(fd, pw, args, opti, true, null);
@@ -9698,17 +9709,102 @@
                 }
                 pw.println();
             }
-            pw.println();
             return true;
         }
         return false;
     }
 
     void dumpBinderProxies(PrintWriter pw) {
+        pw.println("ACTIVITY MANAGER BINDER PROXY STATE (dumpsys activity binder-proxies)");
         dumpBinderProxyInterfaceCounts(pw,
-                "Top proxy interface names held by SYSTEM");
+                "  Top proxy interface names held by SYSTEM");
         dumpBinderProxiesCounts(pw,
-                "Counts of Binder Proxies held by SYSTEM");
+                "  Counts of Binder Proxies held by SYSTEM");
+    }
+
+    void dumpLruEntryLocked(PrintWriter pw, int index, ProcessRecord proc) {
+        pw.print("    #");
+        pw.print(index);
+        pw.print(": ");
+        pw.print(ProcessList.makeOomAdjString(proc.setAdj));
+        pw.print(" ");
+        pw.print(ProcessList.makeProcStateString(proc.getCurProcState()));
+        pw.print(" ");
+        pw.print(proc.toShortString());
+        pw.print(" ");
+        if (proc.hasActivitiesOrRecentTasks() || proc.hasClientActivities()
+                || proc.treatLikeActivity) {
+            pw.print(" activity=");
+            boolean printed = false;
+            if (proc.hasActivities()) {
+                pw.print("activities");
+                printed = true;
+            }
+            if (proc.hasRecentTasks()) {
+                if (printed) {
+                    pw.print("|");
+                }
+                pw.print("recents");
+                printed = true;
+            }
+            if (proc.hasClientActivities()) {
+                if (printed) {
+                    pw.print("|");
+                }
+                pw.print("client");
+                printed = true;
+            }
+            if (proc.treatLikeActivity) {
+                if (printed) {
+                    pw.print("|");
+                }
+                pw.print("treated");
+            }
+        }
+        pw.println();
+    }
+
+    // TODO: Move to ProcessList?
+    void dumpLruLocked(PrintWriter pw, String dumpPackage) {
+        pw.println("ACTIVITY MANAGER LRU PROCESSES (dumpsys activity lru)");
+        final int N = mProcessList.mLruProcesses.size();
+        int i;
+        boolean first = true;
+        for (i = N - 1; i >= mProcessList.mLruProcessActivityStart; i--) {
+            final ProcessRecord r = mProcessList.mLruProcesses.get(i);
+            if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
+                continue;
+            }
+            if (first) {
+                pw.println("  Activities:");
+                first = false;
+            }
+            dumpLruEntryLocked(pw, i, r);
+        }
+        first = true;
+        for (; i >= mProcessList.mLruProcessServiceStart; i--) {
+            final ProcessRecord r = mProcessList.mLruProcesses.get(i);
+            if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
+                continue;
+            }
+            if (first) {
+                pw.println("  Services:");
+                first = false;
+            }
+            dumpLruEntryLocked(pw, i, r);
+        }
+        first = true;
+        for (; i >= 0; i--) {
+            final ProcessRecord r = mProcessList.mLruProcesses.get(i);
+            if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
+                continue;
+            }
+            if (first) {
+                pw.println("  Other:");
+                first = false;
+            }
+            dumpLruEntryLocked(pw, i, r);
+        }
     }
 
     // TODO: Move to ProcessList?
@@ -13214,6 +13310,12 @@
         }
     }
 
+    public void updateServiceGroup(IServiceConnection connection, int group, int importance) {
+        synchronized (this) {
+            mServices.updateServiceGroupLocked(connection, group, importance);
+        }
+    }
+
     public boolean unbindService(IServiceConnection connection) {
         synchronized (this) {
             return mServices.unbindServiceLocked(connection);
@@ -17398,8 +17500,11 @@
         int stepCached = 0;
         int stepEmpty = 0;
         int numCached = 0;
+        int numCachedExtraGroup = 0;
         int numEmpty = 0;
         int numTrimming = 0;
+        int lastCachedGroup = 0;
+        int lastCachedGroupUid = 0;
 
         mNumNonCachedProcs = 0;
         mNumCachedHiddenProcs = 0;
@@ -17523,7 +17628,21 @@
                     case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
                         mNumCachedHiddenProcs++;
                         numCached++;
-                        if (numCached > cachedProcessLimit) {
+                        if (app.connectionGroup != 0) {
+                            if (lastCachedGroupUid == app.uid
+                                    && lastCachedGroup == app.connectionGroup) {
+                                // If this process is the next in the same group, we don't
+                                // want it to count against our limit of the number of cached
+                                // processes, so bump up the group count to account for it.
+                                numCachedExtraGroup++;
+                            } else {
+                                lastCachedGroupUid = app.uid;
+                                lastCachedGroup = app.connectionGroup;
+                            }
+                        } else {
+                            lastCachedGroupUid = lastCachedGroup = 0;
+                        }
+                        if ((numCached - numCachedExtraGroup) > cachedProcessLimit) {
                             app.kill("cached #" + numCached, true);
                         }
                         break;
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 8f8d5ab..67a4d14 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2853,6 +2853,9 @@
             pw.println("    provider [COMP_SPEC]: provider client-side state");
             pw.println("    s[ervices] [COMP_SPEC ...]: service state");
             pw.println("    as[sociations]: tracked app associations");
+            pw.println("    lmk: stats on low memory killer");
+            pw.println("    lru: raw LRU process list");
+            pw.println("    binder-proxies: stats on binder objects and IPCs");
             pw.println("    settings: currently applied config settings");
             pw.println("    service [COMP_SPEC]: service client-side state");
             pw.println("    package [PACKAGE_NAME]: all state related to given package");
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index bfa3f66..aa76b3d 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -48,7 +48,7 @@
     boolean serviceDead;            // Well is it?
 
     // Please keep the following two enum list synced.
-    private static int[] BIND_ORIG_ENUMS = new int[] {
+    private static final int[] BIND_ORIG_ENUMS = new int[] {
             Context.BIND_AUTO_CREATE,
             Context.BIND_DEBUG_UNBIND,
             Context.BIND_NOT_FOREGROUND,
@@ -65,7 +65,7 @@
             Context.BIND_SHOWING_UI,
             Context.BIND_NOT_VISIBLE,
     };
-    private static int[] BIND_PROTO_ENUMS = new int[] {
+    private static final int[] BIND_PROTO_ENUMS = new int[] {
             ConnectionRecordProto.AUTO_CREATE,
             ConnectionRecordProto.DEBUG_UNBIND,
             ConnectionRecordProto.NOT_FG,
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 84b364b..4b19398 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -50,6 +50,7 @@
 import android.app.AppProtoEnums;
 import android.app.IApplicationThread;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
@@ -2204,7 +2205,7 @@
 
     @GuardedBy("mService")
     int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
-            String what, Object obj, ProcessRecord srcApp) {
+            int lruSeq, String what, Object obj, ProcessRecord srcApp) {
         app.lastActivityTime = now;
 
         if (app.hasActivitiesOrRecentTasks()) {
@@ -2225,7 +2226,7 @@
             return index;
         }
 
-        if (lrui >= mLruProcessActivityStart) {
+        if (lrui >= mLruProcessActivityStart && index < mLruProcessActivityStart) {
             // Don't want to touch dependent processes that are hosting activities.
             return index;
         }
@@ -2237,6 +2238,7 @@
         if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
                 + " in LRU list: " + app);
         mLruProcesses.add(index, app);
+        app.lruSeq = lruSeq;
         return index;
     }
 
@@ -2345,9 +2347,11 @@
         */
 
         int nextIndex;
+        int nextActivityIndex = -1;
         if (hasActivity) {
             final int N = mLruProcesses.size();
-            if ((!app.hasActivities() || app.hasRecentTasks())
+            nextIndex = mLruProcessServiceStart;
+            if (!app.hasActivitiesOrRecentTasks() && !app.treatLikeActivity
                     && mLruProcessActivityStart < (N - 1)) {
                 // Process doesn't have activities, but has clients with
                 // activities...  move it up, but one below the top (the top
@@ -2355,36 +2359,92 @@
                 if (DEBUG_LRU) Slog.d(TAG_LRU,
                         "Adding to second-top of LRU activity list: " + app);
                 mLruProcesses.add(N - 1, app);
-                // To keep it from spamming the LRU list (by making a bunch of clients),
-                // we will push down any other entries owned by the app.
+                // If this process is part of a group, need to pull up any other processes
+                // in that group to be with it.
                 final int uid = app.info.uid;
-                for (int i = N - 2; i > mLruProcessActivityStart; i--) {
-                    ProcessRecord subProc = mLruProcesses.get(i);
-                    if (subProc.info.uid == uid) {
-                        // We want to push this one down the list.  If the process after
-                        // it is for the same uid, however, don't do so, because we don't
-                        // want them internally to be re-ordered.
-                        if (mLruProcesses.get(i - 1).info.uid != uid) {
-                            if (DEBUG_LRU) Slog.d(TAG_LRU,
-                                    "Pushing uid " + uid + " swapping at " + i + ": "
-                                            + mLruProcesses.get(i) + " : "
-                                            + mLruProcesses.get(i - 1));
-                            ProcessRecord tmp = mLruProcesses.get(i);
-                            mLruProcesses.set(i, mLruProcesses.get(i - 1));
-                            mLruProcesses.set(i - 1, tmp);
-                            i--;
+                int endIndex = N - 2;
+                nextActivityIndex = N - 2;
+                if (app.connectionGroup > 0) {
+                    int endImportance = app.connectionImportance;
+                    for (int i = endIndex; i >= mLruProcessActivityStart; i--) {
+                        final ProcessRecord subProc = mLruProcesses.get(i);
+                        if (subProc.info.uid == uid
+                                && subProc.connectionGroup == subProc.connectionGroup) {
+                            if (i == endIndex && subProc.connectionImportance >= endImportance) {
+                                // This process is already in the group, and its importance
+                                // is not as strong as the process before it, so it keep it
+                                // correctly positioned in the group.
+                                endIndex--;
+                                endImportance = subProc.connectionImportance;
+                            } else {
+                                // We want to pull this up to be with the rest of the group,
+                                // and order within the group by importance.
+                                boolean moved = false;
+                                for (int pos = N - 1; pos > endIndex; pos--) {
+                                    final ProcessRecord posProc = mLruProcesses.get(pos);
+                                    if (subProc.connectionImportance
+                                            <= posProc.connectionImportance) {
+                                        mLruProcesses.remove(i);
+                                        mLruProcesses.add(pos, subProc);
+                                        moved = true;
+                                        endIndex--;
+                                        break;
+                                    }
+                                }
+                                if (!moved) {
+                                    // Goes to the end of the group.
+                                    mLruProcesses.remove(i);
+                                    mLruProcesses.add(endIndex - 1, subProc);
+                                    endIndex--;
+                                    endImportance = subProc.connectionImportance;
+                                }
+                            }
                         }
-                    } else {
-                        // A gap, we can stop here.
-                        break;
+                    }
+
+                }
+                // To keep it from spamming the LRU list (by making a bunch of clients),
+                // we will distribute other entries owned by it to be in-between other apps.
+                for (int i = endIndex; i >= mLruProcessActivityStart; i--) {
+                    final ProcessRecord subProc = mLruProcesses.get(i);
+                    if (subProc.info.uid != uid) {
+                        // This is a different app...  if we have gone through some of the
+                        // target app, pull this up to be before them.
+                        if (i < endIndex) {
+                            mLruProcesses.remove(i);
+                            mLruProcesses.add(endIndex, subProc);
+                        }
+                        // Find the end of the next group of processes for target app.  This
+                        // is after any entries of different apps (so we don't change the existing
+                        // relative order of apps) and then after the next last group of processes
+                        // of the target app.
+                        for (endIndex--; endIndex >= mLruProcessActivityStart; endIndex--) {
+                            final ProcessRecord endProc = mLruProcesses.get(endIndex);
+                            if (endProc.info.uid == uid) {
+                                break;
+                            }
+                        }
+                        if (endIndex >= mLruProcessActivityStart) {
+                            final ProcessRecord endProc = mLruProcesses.get(endIndex);
+                            for (endIndex--; endIndex >= mLruProcessActivityStart; endIndex--) {
+                                final ProcessRecord nextEndProc = mLruProcesses.get(endIndex);
+                                if (nextEndProc.info.uid != uid
+                                        || nextEndProc.connectionGroup != endProc.connectionGroup) {
+                                    break;
+                                }
+                            }
+                        }
+                        if (i > endIndex) {
+                            i = endIndex;
+                        }
                     }
                 }
             } else {
                 // Process has activities, put it at the very tipsy-top.
                 if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);
                 mLruProcesses.add(app);
+                nextActivityIndex = mLruProcesses.size() - 1;
             }
-            nextIndex = mLruProcessServiceStart;
         } else if (hasService) {
             // Process has services, put it at the top of the service list.
             if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU service list: " + app);
@@ -2416,6 +2476,8 @@
             mLruProcessServiceStart++;
         }
 
+        app.lruSeq = mLruSeq;
+
         // If the app is currently using a content provider or service,
         // bump those processes as well.
         for (int j = app.connections.size() - 1; j >= 0; j--) {
@@ -2423,17 +2485,27 @@
             if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
                     && cr.binding.service.app != null
                     && cr.binding.service.app.lruSeq != mLruSeq
+                    && (cr.flags & Context.BIND_REDUCTION_FLAGS) == 0
                     && !cr.binding.service.app.isPersistent()) {
-                nextIndex = updateLruProcessInternalLocked(cr.binding.service.app,
-                        now,
-                        nextIndex,
-                        "service connection", cr, app);
+                if (cr.binding.service.app.hasClientActivities()) {
+                    if (nextActivityIndex >= 0) {
+                        nextActivityIndex = updateLruProcessInternalLocked(cr.binding.service.app,
+                                now,
+                                nextActivityIndex, mLruSeq,
+                                "service connection", cr, app);
+                    }
+                } else {
+                    nextIndex = updateLruProcessInternalLocked(cr.binding.service.app,
+                            now,
+                            nextIndex, mLruSeq,
+                            "service connection", cr, app);
+                }
             }
         }
         for (int j = app.conProviders.size() - 1; j >= 0; j--) {
             ContentProviderRecord cpr = app.conProviders.get(j).provider;
             if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.isPersistent()) {
-                nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
+                nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex, mLruSeq,
                         "provider reference", cpr, app);
             }
         }
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index faf8561..013de93 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -155,6 +155,9 @@
     int pssStatType;            // The type of stat collection that we are currently requesting
     int savedPriority;          // Previous priority value if we're switching to non-SCHED_OTHER
     int renderThreadTid;        // TID for RenderThread
+    ServiceRecord connectionService; // Service that applied current connectionGroup/Importance
+    int connectionGroup;        // Last group set by a connection
+    int connectionImportance;   // Last importance set by a connection
     boolean serviceb;           // Process currently is on the service B list
     boolean serviceHighRam;     // We are forcing to service B list due to its RAM use
     boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle?
@@ -396,6 +399,11 @@
                     pw.print(" hasAboveClient="); pw.print(hasAboveClient);
                     pw.print(" treatLikeActivity="); pw.println(treatLikeActivity);
         }
+        if (connectionService != null || connectionGroup != 0) {
+            pw.print(prefix); pw.print("connectionGroup="); pw.print(connectionGroup);
+            pw.print(" Importance="); pw.print(connectionImportance);
+            pw.print(" Service="); pw.println(connectionService);
+        }
         if (hasTopUi() || hasOverlayUi() || runningRemoteAnimation) {
             pw.print(prefix); pw.print("hasTopUi="); pw.print(hasTopUi());
                     pw.print(" hasOverlayUi="); pw.print(hasOverlayUi());
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 9d5d65d..1dfb86a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -94,6 +94,7 @@
 import android.service.vr.IVrStateCallbacks;
 import android.text.TextUtils;
 import android.text.style.SuggestionSpan;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.EventLog;
@@ -179,7 +180,6 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Date;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.WeakHashMap;
@@ -321,7 +321,7 @@
     // All known input methods.  mMethodMap also serves as the global
     // lock for this class.
     final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
-    final HashMap<String, InputMethodInfo> mMethodMap = new HashMap<>();
+    final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>();
     private final LruCache<SuggestionSpan, InputMethodInfo> mSecureSuggestionSpans =
             new LruCache<>(SECURE_SUGGESTION_SPANS_MAX_SIZE);
     private final InputMethodSubtypeSwitchingController mSwitchingController;
@@ -457,7 +457,7 @@
         }
     }
 
-    final HashMap<IBinder, ClientState> mClients = new HashMap<>();
+    final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
 
     /**
      * Set once the system is ready to run third party code.
@@ -553,8 +553,8 @@
     private InputMethodSubtype mCurrentSubtype;
 
     // This list contains the pairs of InputMethodInfo and InputMethodSubtype.
-    private final HashMap<InputMethodInfo, ArrayList<InputMethodSubtype>>
-            mShortcutInputMethodsAndSubtypes = new HashMap<>();
+    private final ArrayMap<InputMethodInfo, ArrayList<InputMethodSubtype>>
+            mShortcutInputMethodsAndSubtypes = new ArrayMap<>();
 
     // Was the keyguard locked when this client became current?
     private boolean mCurClientInKeyguard;
@@ -1781,7 +1781,9 @@
         final int callerPid = Binder.getCallingPid();
         synchronized (mMethodMap) {
             // TODO: Optimize this linear search.
-            for (ClientState state : mClients.values()) {
+            final int numClients = mClients.size();
+            for (int i = 0; i < numClients; ++i) {
+                final ClientState state = mClients.valueAt(i);
                 if (state.uid == callerUid && state.pid == callerPid
                         && state.selfReportedDisplayId == selfReportedDisplayId) {
                     throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid
@@ -2192,8 +2194,9 @@
 
     void clearCurMethodLocked() {
         if (mCurMethod != null) {
-            for (ClientState cs : mClients.values()) {
-                clearClientSessionLocked(cs);
+            final int numClients = mClients.size();
+            for (int i = 0; i < numClients; ++i) {
+                clearClientSessionLocked(mClients.valueAt(i));
             }
 
             finishSessionLocked(mEnabledSession);
@@ -3670,7 +3673,7 @@
                         | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS),
                 mSettings.getCurrentUserId());
 
-        final HashMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
+        final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
                 mFileManager.getAllAdditionalInputMethodSubtypes();
         for (int i = 0; i < services.size(); ++i) {
             ResolveInfo ri = services.get(i);
@@ -3824,19 +3827,15 @@
         if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
 
         synchronized (mMethodMap) {
-            final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis =
-                    mSettings.getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(
-                            mContext);
-            if (immis == null || immis.size() == 0) {
+            final List<ImeSubtypeListItem> imList =
+                    mSwitchingController.getSortedInputMethodAndSubtypeListLocked(
+                            showAuxSubtypes, isScreenLocked);
+            if (imList.isEmpty()) {
                 return;
             }
 
             hideInputMethodMenuLocked();
 
-            final List<ImeSubtypeListItem> imList =
-                    mSwitchingController.getSortedInputMethodAndSubtypeListLocked(
-                            showAuxSubtypes, isScreenLocked);
-
             if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
                 final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtypeLocked();
                 if (currentSubtype != null) {
@@ -4310,10 +4309,10 @@
         private static final String ATTR_IS_AUXILIARY = "isAuxiliary";
         private static final String ATTR_IS_ASCII_CAPABLE = "isAsciiCapable";
         private final AtomicFile mAdditionalInputMethodSubtypeFile;
-        private final HashMap<String, InputMethodInfo> mMethodMap;
-        private final HashMap<String, List<InputMethodSubtype>> mAdditionalSubtypesMap =
-                new HashMap<>();
-        public InputMethodFileManager(HashMap<String, InputMethodInfo> methodMap, int userId) {
+        private final ArrayMap<String, InputMethodInfo> mMethodMap;
+        private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypesMap =
+                new ArrayMap<>();
+        InputMethodFileManager(ArrayMap<String, InputMethodInfo> methodMap, int userId) {
             if (methodMap == null) {
                 throw new NullPointerException("methodMap is null");
             }
@@ -4365,15 +4364,15 @@
             }
         }
 
-        public HashMap<String, List<InputMethodSubtype>> getAllAdditionalInputMethodSubtypes() {
+        public ArrayMap<String, List<InputMethodSubtype>> getAllAdditionalInputMethodSubtypes() {
             synchronized (mMethodMap) {
                 return mAdditionalSubtypesMap;
             }
         }
 
         private static void writeAdditionalInputMethodSubtypes(
-                HashMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile,
-                HashMap<String, InputMethodInfo> methodMap) {
+                ArrayMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile,
+                ArrayMap<String, InputMethodInfo> methodMap) {
             // Safety net for the case that this function is called before methodMap is set.
             final boolean isSetMethodMap = methodMap != null && methodMap.size() > 0;
             FileOutputStream fos = null;
@@ -4427,7 +4426,7 @@
         }
 
         private static void readAdditionalInputMethodSubtypes(
-                HashMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile) {
+                ArrayMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile) {
             if (allSubtypes == null || subtypesFile == null) return;
             allSubtypes.clear();
             try (final FileInputStream fis = subtypesFile.openRead()) {
@@ -4625,7 +4624,9 @@
                 info.dump(p, "    ");
             }
             p.println("  Clients:");
-            for (ClientState ci : mClients.values()) {
+            final int numClients = mClients.size();
+            for (int i = 0; i < numClients; ++i) {
+                final ClientState ci = mClients.valueAt(i);
                 p.println("  Client " + ci + ":");
                 p.println("    client=" + ci.client);
                 p.println("    inputContext=" + ci.inputContext);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index 27c59d4..b13c307 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.Printer;
 import android.util.Slog;
@@ -31,8 +32,6 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
@@ -184,11 +183,8 @@
 
         public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(
                 boolean includeAuxiliarySubtypes, boolean isScreenLocked) {
-            final ArrayList<ImeSubtypeListItem> imList = new ArrayList<>();
-            final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis =
-                    mSettings.getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(
-                            mContext);
-            if (immis == null || immis.size() == 0) {
+            final ArrayList<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
+            if (imis.isEmpty()) {
                 return Collections.emptyList();
             }
             if (isScreenLocked && includeAuxiliarySubtypes) {
@@ -197,12 +193,13 @@
                 }
                 includeAuxiliarySubtypes = false;
             }
-            for (InputMethodInfo imi : immis.keySet()) {
-                if (imi == null) {
-                    continue;
-                }
-                List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = immis.get(imi);
-                HashSet<String> enabledSubtypeSet = new HashSet<>();
+            final ArrayList<ImeSubtypeListItem> imList = new ArrayList<>();
+            final int numImes = imis.size();
+            for (int i = 0; i < numImes; ++i) {
+                final InputMethodInfo imi = imis.get(i);
+                final List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList =
+                        mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true);
+                final ArraySet<String> enabledSubtypeSet = new ArraySet<>();
                 for (InputMethodSubtype subtype : explicitlyOrImplicitlyEnabledSubtypeList) {
                     enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
                 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 154e8b3..8e3f351 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -31,6 +31,7 @@
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Pair;
 import android.util.Printer;
 import android.util.Slog;
@@ -45,7 +46,6 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Locale;
@@ -473,7 +473,7 @@
         final int numSubtypes = subtypes.size();
 
         // Handle overridesImplicitlyEnabledSubtype mechanism.
-        final HashMap<String, InputMethodSubtype> applicableModeAndSubtypesMap = new HashMap<>();
+        final ArrayMap<String, InputMethodSubtype> applicableModeAndSubtypesMap = new ArrayMap<>();
         for (int i = 0; i < numSubtypes; ++i) {
             // scan overriding implicitly enabled subtypes.
             final InputMethodSubtype subtype = subtypes.get(i);
@@ -488,8 +488,8 @@
             return new ArrayList<>(applicableModeAndSubtypesMap.values());
         }
 
-        final HashMap<String, ArrayList<InputMethodSubtype>> nonKeyboardSubtypesMap =
-                new HashMap<>();
+        final ArrayMap<String, ArrayList<InputMethodSubtype>> nonKeyboardSubtypesMap =
+                new ArrayMap<>();
         final ArrayList<InputMethodSubtype> keyboardSubtypes = new ArrayList<>();
 
         for (int i = 0; i < numSubtypes; ++i) {
@@ -761,12 +761,12 @@
 
         private final Resources mRes;
         private final ContentResolver mResolver;
-        private final HashMap<String, InputMethodInfo> mMethodMap;
+        private final ArrayMap<String, InputMethodInfo> mMethodMap;
 
         /**
          * On-memory data store to emulate when {@link #mCopyOnWrite} is {@code true}.
          */
-        private final HashMap<String, String> mCopyOnWriteDataStore = new HashMap<>();
+        private final ArrayMap<String, String> mCopyOnWriteDataStore = new ArrayMap<>();
 
         private boolean mCopyOnWrite = false;
         @NonNull
@@ -812,7 +812,7 @@
 
         public InputMethodSettings(
                 Resources res, ContentResolver resolver,
-                HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
+                ArrayMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
                 @UserIdInt int userId, boolean copyOnWrite) {
             mRes = res;
             mResolver = resolver;
@@ -1277,17 +1277,6 @@
             }
         }
 
-        public HashMap<InputMethodInfo, List<InputMethodSubtype>>
-                getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(Context context) {
-            HashMap<InputMethodInfo, List<InputMethodSubtype>> enabledInputMethodAndSubtypes =
-                    new HashMap<>();
-            for (InputMethodInfo imi: getEnabledInputMethodListLocked()) {
-                enabledInputMethodAndSubtypes.put(
-                        imi, getEnabledInputMethodSubtypeListLocked(context, imi, true));
-            }
-            return enabledInputMethodAndSubtypes;
-        }
-
         public void dumpLocked(final Printer pw, final String prefix) {
             pw.println(prefix + "mCurrentUserId=" + mCurrentUserId);
             pw.println(prefix + "mCurrentProfileIds=" + Arrays.toString(mCurrentProfileIds));
diff --git a/services/core/java/com/android/server/inputmethod/LocaleUtils.java b/services/core/java/com/android/server/inputmethod/LocaleUtils.java
index 4958ece..7a6853a 100644
--- a/services/core/java/com/android/server/inputmethod/LocaleUtils.java
+++ b/services/core/java/com/android/server/inputmethod/LocaleUtils.java
@@ -22,10 +22,10 @@
 import android.icu.util.ULocale;
 import android.os.LocaleList;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 
@@ -155,7 +155,7 @@
         }
 
         final int numPreferredLocales = preferredLocales.size();
-        final HashMap<String, ScoreEntry> scoreboard = new HashMap<>();
+        final ArrayMap<String, ScoreEntry> scoreboard = new ArrayMap<>();
         final byte[] score = new byte[numPreferredLocales];
         final ULocale[] preferredULocaleCache = new ULocale[numPreferredLocales];
 
@@ -197,7 +197,11 @@
             }
         }
 
-        final ScoreEntry[] result = scoreboard.values().toArray(new ScoreEntry[scoreboard.size()]);
+        final int numEntries = scoreboard.size();
+        final ScoreEntry[] result = new ScoreEntry[numEntries];
+        for (int i = 0; i < numEntries; ++i) {
+            result[i] = scoreboard.valueAt(i);
+        }
         Arrays.sort(result);
         for (final ScoreEntry entry : result) {
             dest.add(sources.get(entry.mIndex));
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 32990ce..4da29e4 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -411,7 +411,7 @@
     private NotificationAssistants mAssistants;
     private ConditionProviders mConditionProviders;
     private NotificationUsageStats mUsageStats;
-    private boolean mLockScreenAllowSecureNotifications;
+    private boolean mLockScreenAllowSecureNotifications = true;
 
     private static final int MY_UID = Process.myUid();
     private static final int MY_PID = Process.myPid();
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 31711e5..6f275ec 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2821,7 +2821,8 @@
         pw.println("       [--install-reason 0/1/2/3/4] [--originating-uri URI]");
         pw.println("       [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
         pw.println("       [--preload] [--instantapp] [--full] [--dont-kill]");
-        pw.println("       [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES] [PATH|-]");
+        pw.println("       [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES] [--apex]");
+        pw.println("       [PATH|-]");
         pw.println("    Install an application.  Must provide the apk data to install, either as a");
         pw.println("    file path or '-' to read from stdin.  Options are:");
         pw.println("      -l: forward lock application");
@@ -2850,13 +2851,14 @@
         pw.println("      --force-uuid: force install on to disk volume with given UUID");
         pw.println("      --force-sdk: allow install even when existing app targets platform");
         pw.println("          codename but new one targets a final API level");
+        pw.println("      --apex: install an .apex file, not an .apk");
         pw.println("");
         pw.println("  install-create [-lrtsfdg] [-i PACKAGE] [--user USER_ID|all|current]");
         pw.println("       [-p INHERIT_PACKAGE] [--install-location 0/1/2]");
         pw.println("       [--install-reason 0/1/2/3/4] [--originating-uri URI]");
         pw.println("       [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
         pw.println("       [--preload] [--instantapp] [--full] [--dont-kill]");
-        pw.println("       [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
+        pw.println("       [--force-uuid internal|UUID] [--pkg PACKAGE] [--apex] [-S BYTES]");
         pw.println("       [--multi-package]");
         pw.println("    Like \"install\", but starts an install session.  Use \"install-write\"");
         pw.println("    to push data into the session, and \"install-commit\" to finish.");
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 812fd82..79e2688 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.power;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.thermal.V1_0.ThermalStatus;
 import android.hardware.thermal.V1_0.ThermalStatusCode;
@@ -26,12 +27,16 @@
 import android.os.HwBinder;
 import android.os.IThermalEventListener;
 import android.os.IThermalService;
+import android.os.IThermalStatusListener;
 import android.os.PowerManager;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.Temperature;
+import android.util.ArrayMap;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
 import com.android.server.FgThread;
 import com.android.server.SystemService;
@@ -39,6 +44,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.NoSuchElementException;
 
@@ -51,210 +57,154 @@
 public class ThermalManagerService extends SystemService {
     private static final String TAG = ThermalManagerService.class.getSimpleName();
 
-    /** Registered observers of the thermal changed events. Cookie is used to store type */
+    /** Lock to protect listen list. */
+    private final Object mLock = new Object();
+
+    /**
+     * Registered observers of the thermal events. Cookie is used to store type as Integer, null
+     * means no filter.
+     */
     @GuardedBy("mLock")
     private final RemoteCallbackList<IThermalEventListener> mThermalEventListeners =
             new RemoteCallbackList<>();
 
-    /** Lock to protect HAL handles and listen list. */
-    private final Object mLock = new Object();
-
-    /** Newly registered callback. */
+    /** Registered observers of the thermal status. */
     @GuardedBy("mLock")
-    private IThermalEventListener mNewListenerCallback = null;
+    private final RemoteCallbackList<IThermalStatusListener> mThermalStatusListeners =
+            new RemoteCallbackList<>();
 
-    /** Newly registered callback type, null means not filter type. */
+    /** Current thermal status */
     @GuardedBy("mLock")
-    private Integer mNewListenerType = null;
+    private int mStatus;
+
+    /** Current thermal map, key as name */
+    @GuardedBy("mLock")
+    private ArrayMap<String, Temperature> mTemperatureMap = new ArrayMap<>();
 
     /** Local PMS handle. */
     private final PowerManager mPowerManager;
 
-    /** Proxy object for the Thermal HAL 2.0 service. */
+    /** HAL wrapper. */
+    private ThermalHalWrapper mHalWrapper;
+
+    /** Hal ready. */
     @GuardedBy("mLock")
-    private android.hardware.thermal.V2_0.IThermal mThermalHal20 = null;
+    private boolean mHalReady;
 
-    /** Proxy object for the Thermal HAL 1.1 service. */
-    @GuardedBy("mLock")
-    private android.hardware.thermal.V1_1.IThermal mThermalHal11 = null;
-
-    /** Cookie for matching the right end point. */
-    private static final int THERMAL_HAL_DEATH_COOKIE = 5612;
-
-    /** HWbinder callback for Thermal HAL 2.0. */
-    private final IThermalChangedCallback.Stub mThermalCallback20 =
-            new IThermalChangedCallback.Stub() {
-                @Override
-                public void notifyThrottling(
-                        android.hardware.thermal.V2_0.Temperature temperature) {
-                    android.os.Temperature thermalSvcTemp = new android.os.Temperature(
-                            temperature.value, temperature.type, temperature.name,
-                            temperature.throttlingStatus);
-                    final long token = Binder.clearCallingIdentity();
-                    try {
-                        notifyThrottlingImpl(thermalSvcTemp);
-                    } finally {
-                        Binder.restoreCallingIdentity(token);
-                    }
-                }
-            };
-
-    /** HWbinder callback for Thermal HAL 1.1. */
-    private final IThermalCallback.Stub mThermalCallback11 =
-            new IThermalCallback.Stub() {
-                @Override
-                public void notifyThrottling(boolean isThrottling,
-                        android.hardware.thermal.V1_0.Temperature temperature) {
-                    android.os.Temperature thermalSvcTemp = new android.os.Temperature(
-                            temperature.currentValue, temperature.type, temperature.name,
-                            isThrottling ? ThrottlingSeverity.SEVERE : ThrottlingSeverity.NONE);
-                    final long token = Binder.clearCallingIdentity();
-                    try {
-                        notifyThrottlingImpl(thermalSvcTemp);
-                    } finally {
-                        Binder.restoreCallingIdentity(token);
-                    }
-                }
-            };
+    /** Invalid throttling status */
+    private static final int INVALID_THROTTLING = Integer.MIN_VALUE;
 
     public ThermalManagerService(Context context) {
+        this(context, null);
+    }
+
+    @VisibleForTesting
+    ThermalManagerService(Context context, @Nullable ThermalHalWrapper halWrapper) {
         super(context);
         mPowerManager = context.getSystemService(PowerManager.class);
+        mHalWrapper = halWrapper;
+        // Initialize to invalid to send status onActivityManagerReady
+        mStatus = INVALID_THROTTLING;
     }
 
-    private void setNewListener(IThermalEventListener listener, Integer type) {
-        synchronized (mLock) {
-            mNewListenerCallback = listener;
-            mNewListenerType = type;
+    @Override
+    public void onStart() {
+        publishBinderService(Context.THERMAL_SERVICE, mService);
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+            onActivityManagerReady();
         }
     }
 
-    private void clearNewListener() {
+    private void onActivityManagerReady() {
         synchronized (mLock) {
-            mNewListenerCallback = null;
-            mNewListenerType = null;
-        }
-    }
-
-    private final IThermalService.Stub mService = new IThermalService.Stub() {
-        @Override
-        public void registerThermalEventListener(IThermalEventListener listener) {
-            synchronized (mLock) {
-                mThermalEventListeners.register(listener, null);
-                // Notify its callback after new client registered.
-                setNewListener(listener, null);
-                long token = Binder.clearCallingIdentity();
-                try {
-                    notifyCurrentTemperaturesLocked();
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                    clearNewListener();
+            // Connect to HAL and post to listeners.
+            boolean halConnected = (mHalWrapper != null);
+            if (!halConnected) {
+                mHalWrapper = new ThermalHal20Wrapper();
+                halConnected = mHalWrapper.connectToHal();
+                if (!halConnected) {
+                    mHalWrapper = new ThermalHal11Wrapper();
+                    halConnected = mHalWrapper.connectToHal();
                 }
             }
-        }
-
-        @Override
-        public void registerThermalEventListenerWithType(IThermalEventListener listener, int type) {
-            synchronized (mLock) {
-                mThermalEventListeners.register(listener, new Integer(type));
-                setNewListener(listener, new Integer(type));
-                // Notify its callback after new client registered.
-                long token = Binder.clearCallingIdentity();
-                try {
-                    notifyCurrentTemperaturesLocked();
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                    clearNewListener();
-                }
+            mHalWrapper.setCallback(this::onTemperatureChangedCallback);
+            if (!halConnected) {
+                return;
             }
-        }
-
-        @Override
-        public void unregisterThermalEventListener(IThermalEventListener listener) {
-            synchronized (mLock) {
-                long token = Binder.clearCallingIdentity();
-                try {
-                    mThermalEventListeners.unregister(listener);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                }
+            List<Temperature> temperatures = mHalWrapper.getCurrentTemperatures(false,
+                    0);
+            final int count = temperatures.size();
+            for (int i = 0; i < count; i++) {
+                onTemperatureChanged(temperatures.get(i), false);
             }
+            onTemperatureMapChangedLocked();
+            mHalReady = halConnected /* true */;
         }
-
-        @Override
-        public List<android.os.Temperature> getCurrentTemperatures() {
-            List<android.os.Temperature> ret;
-            long token = Binder.clearCallingIdentity();
-            try {
-                ret = getCurrentTemperaturesInternal(false, 0 /* not used */);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-            return ret;
-        }
-
-        @Override
-        public List<android.os.Temperature> getCurrentTemperaturesWithType(int type) {
-            List<android.os.Temperature> ret;
-            long token = Binder.clearCallingIdentity();
-            try {
-                ret = getCurrentTemperaturesInternal(true, type);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-            return ret;
-        }
-
-        @Override
-        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-            if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
-            pw.println("ThermalEventListeners dump:");
-            synchronized (mLock) {
-                mThermalEventListeners.dump(pw, "\t");
-                pw.println("ThermalHAL 1.1 connected: " + (mThermalHal11 != null ? "yes" : "no"));
-                pw.println("ThermalHAL 2.0 connected: " + (mThermalHal20 != null ? "yes" : "no"));
-            }
-        }
-    };
-
-    private List<android.os.Temperature> getCurrentTemperaturesInternal(boolean shouldFilter,
-            int type) {
-        List<android.os.Temperature> ret = new ArrayList<>();
-        synchronized (mLock) {
-            if (mThermalHal20 == null) {
-                return ret;
-            }
-            try {
-                mThermalHal20.getCurrentTemperatures(shouldFilter, type,
-                        (ThermalStatus status,
-                                ArrayList<android.hardware.thermal.V2_0.Temperature>
-                                        temperatures) -> {
-                            if (ThermalStatusCode.SUCCESS == status.code) {
-                                for (android.hardware.thermal.V2_0.Temperature
-                                        temperature : temperatures) {
-                                    ret.add(new android.os.Temperature(
-                                            temperature.value, temperature.type, temperature.name,
-                                            temperature.throttlingStatus));
-                                }
-                            } else {
-                                Slog.e(TAG,
-                                        "Couldn't get temperatures because of HAL error: "
-                                                + status.debugMessage);
-                            }
-
-                        });
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e);
-                connectToHalLocked();
-                // Post to listeners after reconnect to HAL.
-                notifyCurrentTemperaturesLocked();
-            }
-        }
-        return ret;
     }
 
-    private void notifyListener(android.os.Temperature temperature, IThermalEventListener listener,
-            Integer type) {
+    private void postStatusListener(IThermalStatusListener listener) {
+        final boolean thermalCallbackQueued = FgThread.getHandler().post(() -> {
+            try {
+                listener.onStatusChange(mStatus);
+            } catch (RemoteException | RuntimeException e) {
+                Slog.e(TAG, "Thermal callback failed to call", e);
+            }
+        });
+        if (!thermalCallbackQueued) {
+            Slog.e(TAG, "Thermal callback failed to queue");
+        }
+    }
+
+    private void notifyStatusListenersLocked() {
+        if (!Temperature.isValidStatus(mStatus)) {
+            return;
+        }
+        final int length = mThermalStatusListeners.beginBroadcast();
+        try {
+            for (int i = 0; i < length; i++) {
+                final IThermalStatusListener listener =
+                        mThermalStatusListeners.getBroadcastItem(i);
+                postStatusListener(listener);
+            }
+        } finally {
+            mThermalStatusListeners.finishBroadcast();
+        }
+    }
+
+    private void onTemperatureMapChangedLocked() {
+        int newStatus = INVALID_THROTTLING;
+        final int count = mTemperatureMap.size();
+        for (int i = 0; i < count; i++) {
+            Temperature t = mTemperatureMap.valueAt(i);
+            if (t.getStatus() >= newStatus) {
+                newStatus = t.getStatus();
+            }
+        }
+        if (newStatus != mStatus) {
+            mStatus = newStatus;
+            notifyStatusListenersLocked();
+        }
+    }
+
+
+    private void postEventListenerCurrentTemperatures(IThermalEventListener listener,
+            @Nullable Integer type) {
+        synchronized (mLock) {
+            final int count = mTemperatureMap.size();
+            for (int i = 0; i < count; i++) {
+                postEventListener(mTemperatureMap.valueAt(i), listener,
+                        type);
+            }
+        }
+    }
+
+    private void postEventListener(Temperature temperature,
+            IThermalEventListener listener,
+            @Nullable Integer type) {
         // Skip if listener registered with a different type
         if (type != null && type != temperature.getType()) {
             return;
@@ -271,11 +221,26 @@
         }
     }
 
-    private void notifyThrottlingImpl(android.os.Temperature temperature) {
+    private void notifyEventListenersLocked(Temperature temperature) {
+        final int length = mThermalEventListeners.beginBroadcast();
+        try {
+            for (int i = 0; i < length; i++) {
+                final IThermalEventListener listener =
+                        mThermalEventListeners.getBroadcastItem(i);
+                final Integer type =
+                        (Integer) mThermalEventListeners.getBroadcastCookie(i);
+                postEventListener(temperature, listener, type);
+            }
+        } finally {
+            mThermalEventListeners.finishBroadcast();
+        }
+    }
+
+    private void onTemperatureChanged(Temperature temperature, boolean sendStatus) {
         synchronized (mLock) {
             // Thermal Shutdown for Skin temperature
-            if (temperature.getStatus() == android.os.Temperature.THROTTLING_SHUTDOWN
-                    && temperature.getType() == android.os.Temperature.TYPE_SKIN) {
+            if (temperature.getStatus() == Temperature.THROTTLING_SHUTDOWN
+                    && temperature.getType() == Temperature.TYPE_SKIN) {
                 final long token = Binder.clearCallingIdentity();
                 try {
                     mPowerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
@@ -284,107 +249,420 @@
                 }
             }
 
-            if (mNewListenerCallback != null) {
-                // Only notify current newly added callback.
-                notifyListener(temperature, mNewListenerCallback, mNewListenerType);
+            Temperature old = mTemperatureMap.put(temperature.getName(), temperature);
+            if (old != null) {
+                if (old.getStatus() != temperature.getStatus()) {
+                    notifyEventListenersLocked(temperature);
+                }
             } else {
-                final int length = mThermalEventListeners.beginBroadcast();
-                try {
-                    for (int i = 0; i < length; i++) {
-                        final IThermalEventListener listener =
-                                mThermalEventListeners.getBroadcastItem(i);
-                        final Integer type = (Integer) mThermalEventListeners.getBroadcastCookie(i);
-                        notifyListener(temperature, listener, type);
-                    }
-                } finally {
-                    mThermalEventListeners.finishBroadcast();
-                }
+                notifyEventListenersLocked(temperature);
+            }
+            if (sendStatus) {
+                onTemperatureMapChangedLocked();
             }
         }
     }
 
-    @Override
-    public void onStart() {
-        publishBinderService(Context.THERMAL_SERVICE, mService);
+    private void onTemperatureChangedCallback(Temperature temperature) {
+        onTemperatureChanged(temperature, true);
     }
 
-    @Override
-    public void onBootPhase(int phase) {
-        if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
-            onActivityManagerReady();
+    private void dumpTemperaturesLocked(PrintWriter pw, String prefix,
+            Collection<Temperature> temperatures) {
+        for (Temperature t : temperatures) {
+            pw.print(prefix);
+            String out = String.format("Name: %s, Type: %d, Status: %d, Value: %f",
+                    t.getName(),
+                    t.getType(),
+                    t.getStatus(),
+                    t.getValue()
+            );
+            pw.println(out);
         }
     }
 
-    private void notifyCurrentTemperaturesCallbackLocked(ThermalStatus status,
-            ArrayList<android.hardware.thermal.V2_0.Temperature> temperatures) {
-        if (ThermalStatusCode.SUCCESS != status.code) {
-            Slog.e(TAG, "Couldn't get temperatures because of HAL error: "
-                    + status.debugMessage);
-            return;
-        }
-        for (android.hardware.thermal.V2_0.Temperature temperature : temperatures) {
-            android.os.Temperature thermal_svc_temp =
-                    new android.os.Temperature(
-                            temperature.value, temperature.type,
-                            temperature.name,
-                            temperature.throttlingStatus);
-            notifyThrottlingImpl(thermal_svc_temp);
-        }
-    }
-
-    private void notifyCurrentTemperaturesLocked() {
-        if (mThermalHal20 == null) {
-            return;
-        }
-        try {
-            mThermalHal20.getCurrentTemperatures(false, 0,
-                    this::notifyCurrentTemperaturesCallbackLocked);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Couldn't get temperatures, reconnecting...", e);
-            connectToHalLocked();
-        }
-    }
-
-    private void onActivityManagerReady() {
-        synchronized (mLock) {
-            connectToHalLocked();
-            // Post to listeners after connect to HAL.
-            notifyCurrentTemperaturesLocked();
-        }
-    }
-
-    final class DeathRecipient implements HwBinder.DeathRecipient {
+    @VisibleForTesting
+    final IThermalService.Stub mService = new IThermalService.Stub() {
         @Override
-        public void serviceDied(long cookie) {
-            if (cookie == THERMAL_HAL_DEATH_COOKIE) {
-                Slog.e(TAG, "Thermal HAL service died cookie: " + cookie);
+        public boolean registerThermalEventListener(IThermalEventListener listener) {
+            synchronized (mLock) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    if (!mThermalEventListeners.register(listener, null)) {
+                        return false;
+                    }
+                    if (mHalReady) {
+                        // Notify its callback after new client registered.
+                        postEventListenerCurrentTemperatures(listener, null);
+                    }
+                    return true;
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        }
+
+        @Override
+        public boolean registerThermalEventListenerWithType(IThermalEventListener listener,
+                int type) {
+            synchronized (mLock) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    if (!mThermalEventListeners.register(listener, new Integer(type))) {
+                        return false;
+                    }
+                    if (mHalReady) {
+                        // Notify its callback after new client registered.
+                        postEventListenerCurrentTemperatures(listener, new Integer(type));
+                    }
+                    return true;
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        }
+
+        @Override
+        public boolean unregisterThermalEventListener(IThermalEventListener listener) {
+            synchronized (mLock) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    return mThermalEventListeners.unregister(listener);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        }
+
+        @Override
+        public List<Temperature> getCurrentTemperatures() {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (!mHalReady) {
+                    return new ArrayList<>();
+                }
+                return mHalWrapper.getCurrentTemperatures(false, 0 /* not used */);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public List<Temperature> getCurrentTemperaturesWithType(int type) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (!mHalReady) {
+                    return new ArrayList<>();
+                }
+                return mHalWrapper.getCurrentTemperatures(true, type);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public boolean registerThermalStatusListener(IThermalStatusListener listener) {
+            synchronized (mLock) {
+                // Notify its callback after new client registered.
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    if (!mThermalStatusListeners.register(listener)) {
+                        return false;
+                    }
+                    if (mHalReady) {
+                        // Notify its callback after new client registered.
+                        postStatusListener(listener);
+                    }
+                    return true;
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        }
+
+        @Override
+        public boolean unregisterThermalStatusListener(IThermalStatusListener listener) {
+            synchronized (mLock) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    return mThermalStatusListeners.unregister(listener);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        }
+
+        @Override
+        public int getCurrentStatus() {
+            synchronized (mLock) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    return Temperature.isValidStatus(mStatus) ? mStatus
+                            : Temperature.THROTTLING_NONE;
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        }
+
+        @Override
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
+                return;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
                 synchronized (mLock) {
-                    connectToHalLocked();
-                    // Post to listeners after reconnect to HAL.
-                    notifyCurrentTemperaturesLocked();
+                    pw.println("ThermalEventListeners:");
+                    mThermalEventListeners.dump(pw, "\t");
+                    pw.println("ThermalStatusListeners:");
+                    mThermalStatusListeners.dump(pw, "\t");
+                    pw.println("Thermal Status: " + Integer.toString(mStatus));
+                    pw.println("Cached temperatures:");
+                    dumpTemperaturesLocked(pw, "\t", mTemperatureMap.values());
+                    pw.println("HAL Ready: " + Boolean.toString(mHalReady));
+                    if (mHalReady) {
+                        pw.println("HAL connection:");
+                        mHalWrapper.dump(pw, "\t");
+                        pw.println("Current temperatures from HAL:");
+                        dumpTemperaturesLocked(pw, "\t",
+                                mHalWrapper.getCurrentTemperatures(false, 0));
+                    }
+                }
+
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    };
+
+    abstract static class ThermalHalWrapper {
+        protected static final String TAG = ThermalHalWrapper.class.getSimpleName();
+
+        /** Lock to protect HAL handle. */
+        protected final Object mHalLock = new Object();
+
+        @FunctionalInterface
+        interface TemperatureChangedCallback {
+            void onValues(Temperature temperature);
+        }
+
+        /** Temperature callback. */
+        protected TemperatureChangedCallback mCallback;
+
+        /** Cookie for matching the right end point. */
+        protected static final int THERMAL_HAL_DEATH_COOKIE = 5612;
+
+        @VisibleForTesting
+        protected void setCallback(TemperatureChangedCallback cb) {
+            mCallback = cb;
+        }
+
+        protected abstract List<Temperature> getCurrentTemperatures(boolean shouldFilter,
+                int type);
+
+        protected abstract boolean connectToHal();
+
+        protected abstract void dump(PrintWriter pw, String prefix);
+
+        protected void resendCurrentTemperatures() {
+            synchronized (mHalLock) {
+                List<Temperature> temperatures = getCurrentTemperatures(false, 0);
+                final int count = temperatures.size();
+                for (int i = 0; i < count; i++) {
+                    mCallback.onValues(temperatures.get(i));
+                }
+            }
+        }
+
+        final class DeathRecipient implements HwBinder.DeathRecipient {
+            @Override
+            public void serviceDied(long cookie) {
+                if (cookie == THERMAL_HAL_DEATH_COOKIE) {
+                    Slog.e(TAG, "Thermal HAL service died cookie: " + cookie);
+                    synchronized (mHalLock) {
+                        connectToHal();
+                        // Post to listeners after reconnect to HAL.
+                        resendCurrentTemperatures();
+                    }
                 }
             }
         }
     }
 
-    private void connectToHalLocked() {
-        try {
-            mThermalHal20 = android.hardware.thermal.V2_0.IThermal.getService();
-            mThermalHal20.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE);
-            mThermalHal20.registerThermalChangedCallback(mThermalCallback20, false,
-                    0 /* not used */);
-        } catch (NoSuchElementException | RemoteException e) {
-            Slog.e(TAG, "Thermal HAL 2.0 service not connected, trying 1.1.");
-            mThermalHal20 = null;
-            try {
-                mThermalHal11 = android.hardware.thermal.V1_1.IThermal.getService();
-                mThermalHal11.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE);
-                mThermalHal11.registerThermalCallback(mThermalCallback11);
-            } catch (NoSuchElementException | RemoteException e2) {
-                Slog.e(TAG,
-                        "Thermal HAL 1.1 service not connected, no thermal call back "
-                                + "will be called.");
-                mThermalHal11 = null;
+    static class ThermalHal11Wrapper extends ThermalHalWrapper {
+        /** Proxy object for the Thermal HAL 1.1 service. */
+        @GuardedBy("mHalLock")
+        private android.hardware.thermal.V1_1.IThermal mThermalHal11 = null;
+
+        /** HWbinder callback for Thermal HAL 1.1. */
+        private final IThermalCallback.Stub mThermalCallback11 =
+                new IThermalCallback.Stub() {
+                    @Override
+                    public void notifyThrottling(boolean isThrottling,
+                            android.hardware.thermal.V1_0.Temperature temperature) {
+                        Temperature thermalSvcTemp = new Temperature(
+                                temperature.currentValue, temperature.type, temperature.name,
+                                isThrottling ? ThrottlingSeverity.SEVERE
+                                        : ThrottlingSeverity.NONE);
+                        final long token = Binder.clearCallingIdentity();
+                        try {
+                            mCallback.onValues(thermalSvcTemp);
+                        } finally {
+                            Binder.restoreCallingIdentity(token);
+                        }
+                    }
+                };
+
+        @Override
+        protected List<Temperature> getCurrentTemperatures(boolean shouldFilter,
+                int type) {
+            synchronized (mHalLock) {
+                List<Temperature> ret = new ArrayList<>();
+                if (mThermalHal11 == null) {
+                    return ret;
+                }
+                try {
+                    mThermalHal11.getTemperatures(
+                            (ThermalStatus status,
+                                    ArrayList<android.hardware.thermal.V1_0.Temperature>
+                                            temperatures) -> {
+                                if (ThermalStatusCode.SUCCESS == status.code) {
+                                    for (android.hardware.thermal.V1_0.Temperature
+                                            temperature : temperatures) {
+                                        if (shouldFilter && type != temperature.type) {
+                                            continue;
+                                        }
+                                        // Thermal HAL 1.1 doesn't report current throttling status
+                                        ret.add(new Temperature(
+                                                temperature.currentValue, temperature.type,
+                                                temperature.name,
+                                                Temperature.THROTTLING_NONE));
+                                    }
+                                } else {
+                                    Slog.e(TAG,
+                                            "Couldn't get temperatures because of HAL error: "
+                                                    + status.debugMessage);
+                                }
+
+                            });
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e);
+                    connectToHal();
+                }
+                return ret;
+            }
+        }
+
+        @Override
+        protected boolean connectToHal() {
+            synchronized (mHalLock) {
+                try {
+                    mThermalHal11 = android.hardware.thermal.V1_1.IThermal.getService();
+                    mThermalHal11.linkToDeath(new DeathRecipient(),
+                            THERMAL_HAL_DEATH_COOKIE);
+                    mThermalHal11.registerThermalCallback(mThermalCallback11);
+                } catch (NoSuchElementException | RemoteException e) {
+                    Slog.e(TAG,
+                            "Thermal HAL 1.1 service not connected, no thermal call back will be "
+                                    + "called.");
+                    mThermalHal11 = null;
+                }
+                return (mThermalHal11 != null);
+            }
+        }
+
+        @Override
+        protected void dump(PrintWriter pw, String prefix) {
+            synchronized (mHalLock) {
+                pw.print(prefix);
+                pw.println("ThermalHAL 1.1 connected: " + (mThermalHal11 != null ? "yes"
+                        : "no"));
+            }
+        }
+    }
+
+    static class ThermalHal20Wrapper extends ThermalHalWrapper {
+        /** Proxy object for the Thermal HAL 2.0 service. */
+        @GuardedBy("mHalLock")
+        private android.hardware.thermal.V2_0.IThermal mThermalHal20 = null;
+
+        /** HWbinder callback for Thermal HAL 2.0. */
+        private final IThermalChangedCallback.Stub mThermalCallback20 =
+                new IThermalChangedCallback.Stub() {
+                    @Override
+                    public void notifyThrottling(
+                            android.hardware.thermal.V2_0.Temperature temperature) {
+                        Temperature thermalSvcTemp = new Temperature(
+                                temperature.value, temperature.type, temperature.name,
+                                temperature.throttlingStatus);
+                        final long token = Binder.clearCallingIdentity();
+                        try {
+                            mCallback.onValues(thermalSvcTemp);
+                        } finally {
+                            Binder.restoreCallingIdentity(token);
+                        }
+                    }
+                };
+
+        @Override
+        protected List<Temperature> getCurrentTemperatures(boolean shouldFilter,
+                int type) {
+            synchronized (mHalLock) {
+                List<Temperature> ret = new ArrayList<>();
+                if (mThermalHal20 == null) {
+                    return ret;
+                }
+                try {
+                    mThermalHal20.getCurrentTemperatures(shouldFilter, type,
+                            (ThermalStatus status,
+                                    ArrayList<android.hardware.thermal.V2_0.Temperature>
+                                            temperatures) -> {
+                                if (ThermalStatusCode.SUCCESS == status.code) {
+                                    for (android.hardware.thermal.V2_0.Temperature
+                                            temperature : temperatures) {
+                                        ret.add(new Temperature(
+                                                temperature.value, temperature.type,
+                                                temperature.name,
+                                                temperature.throttlingStatus));
+                                    }
+                                } else {
+                                    Slog.e(TAG,
+                                            "Couldn't get temperatures because of HAL error: "
+                                                    + status.debugMessage);
+                                }
+
+                            });
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e);
+                    connectToHal();
+                }
+                return ret;
+            }
+        }
+
+        @Override
+        protected boolean connectToHal() {
+            synchronized (mHalLock) {
+                try {
+                    mThermalHal20 = android.hardware.thermal.V2_0.IThermal.getService();
+                    mThermalHal20.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE);
+                    mThermalHal20.registerThermalChangedCallback(mThermalCallback20, false,
+                            0 /* not used */);
+                } catch (NoSuchElementException | RemoteException e) {
+                    Slog.e(TAG, "Thermal HAL 2.0 service not connected, trying 1.1.");
+                    mThermalHal20 = null;
+                }
+                return (mThermalHal20 != null);
+            }
+        }
+
+        @Override
+        protected void dump(PrintWriter pw, String prefix) {
+            synchronized (mHalLock) {
+                pw.print(prefix);
+                pw.println("ThermalHAL 2.0 connected: " + (mThermalHal20 != null ? "yes"
+                        : "no"));
             }
         }
     }
diff --git a/services/core/java/com/android/server/role/RemoteRoleControllerService.java b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
index 538f4cf..cb89780 100644
--- a/services/core/java/com/android/server/role/RemoteRoleControllerService.java
+++ b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
@@ -48,6 +48,7 @@
     static final boolean DEBUG = false;
     private static final String LOG_TAG = RemoteRoleControllerService.class.getSimpleName();
 
+    // TODO: STOPSHIP: This isn't the right thread, as we are also using it to write to disk.
     @NonNull
     private static final Handler sCallbackHandler = BackgroundThread.getHandler();
 
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index d01e762..4124210 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -284,12 +284,26 @@
         }
 
         @Override
+        public void setRoleNamesFromController(@NonNull List<String> roleNames) {
+            Preconditions.checkNotNull(roleNames, "roleNames cannot be null");
+            getContext().enforceCallingOrSelfPermission(
+                    RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
+                    "setRoleNamesFromController");
+
+            int userId = UserHandle.getCallingUserId();
+            synchronized (mLock) {
+                RoleUserState userState = getUserStateLocked(userId);
+                userState.setRoleNamesLocked(roleNames);
+            }
+        }
+
+        @Override
         public boolean addRoleHolderFromController(@NonNull String roleName,
                 @NonNull String packageName) {
             Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
             Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
             getContext().enforceCallingOrSelfPermission(
-                    RoleManager.PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER,
+                    RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
                     "addRoleHolderFromController");
 
             int userId = UserHandle.getCallingUserId();
@@ -305,7 +319,7 @@
             Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
             Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
             getContext().enforceCallingOrSelfPermission(
-                    RoleManager.PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER,
+                    RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
                     "removeRoleHolderFromController");
 
             int userId = UserHandle.getCallingUserId();
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index 68e737e..9c43f4d 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -45,6 +45,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+import java.util.List;
 
 /**
  * Stores the state of roles for a user.
@@ -101,7 +102,11 @@
     @GuardedBy("RoleManagerService.mLock")
     public void setVersionLocked(int version) {
         throwIfDestroyedLocked();
+        if (mVersion == version) {
+            return;
+        }
         mVersion = version;
+        writeAsyncLocked();
     }
 
     /**
@@ -132,6 +137,41 @@
     }
 
     /**
+     * Set the names of all available roles.
+     *
+     * @param roleNames the names of all the available roles
+     */
+    @GuardedBy("RoleManagerService.mLock")
+    public void setRoleNamesLocked(@NonNull List<String> roleNames) {
+        throwIfDestroyedLocked();
+        boolean changed = false;
+        for (int i = mRoles.size() - 1; i >= 0; i--) {
+            String roleName = mRoles.keyAt(i);
+            if (!roleNames.contains(roleName)) {
+                ArraySet<String> packageNames = mRoles.valueAt(i);
+                if (!packageNames.isEmpty()) {
+                    Slog.e(LOG_TAG, "Holders of a removed role should have been cleaned up, role: "
+                            + roleName + ", holders: " + packageNames);
+                }
+                mRoles.removeAt(i);
+                changed = true;
+            }
+        }
+        int roleNamesSize = roleNames.size();
+        for (int i = 0; i < roleNamesSize; i++) {
+            String roleName = roleNames.get(i);
+            if (!mRoles.containsKey(roleName)) {
+                mRoles.put(roleName, new ArraySet<>());
+                Slog.i(LOG_TAG, "Added new role: " + roleName);
+                changed = true;
+            }
+        }
+        if (changed) {
+            writeAsyncLocked();
+        }
+    }
+
+    /**
      * Add a holder to a role.
      *
      * @param roleName the name of the role to add the holder to
@@ -150,7 +190,10 @@
                     + ", package: " + packageName);
             return false;
         }
-        roleHolders.add(packageName);
+        boolean changed = roleHolders.add(packageName);
+        if (changed) {
+            writeAsyncLocked();
+        }
         return true;
     }
 
@@ -173,7 +216,10 @@
                     + ", package: " + packageName);
             return false;
         }
-        roleHolders.remove(packageName);
+        boolean changed = roleHolders.remove(packageName);
+        if (changed) {
+            writeAsyncLocked();
+        }
         return true;
     }
 
@@ -181,7 +227,7 @@
      * Schedule writing the state to file.
      */
     @GuardedBy("RoleManagerService.mLock")
-    public void writeAsyncLocked() {
+    private void writeAsyncLocked() {
         throwIfDestroyedLocked();
         int version = mVersion;
         ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
@@ -192,6 +238,7 @@
             roles.put(roleName, roleHolders);
         }
         mWriteHandler.removeCallbacksAndMessages(null);
+        // TODO: Throttle writes.
         mWriteHandler.sendMessage(PooledLambda.obtainMessage(
                 RoleUserState::writeSync, this, version, roles));
     }
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index d2ca850..6f6846d 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -1630,37 +1630,42 @@
         if (this.mKernelCpuThreadReader == null) {
             return;
         }
-        KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = this.mKernelCpuThreadReader
-                .getCurrentProcessCpuUsage();
-        if (processCpuUsage == null) {
+        ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages =
+                this.mKernelCpuThreadReader.getProcessCpuUsageByUids();
+        if (processCpuUsages == null) {
             return;
         }
         int[] cpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz();
-        for (KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage
-                : processCpuUsage.threadCpuUsages) {
-            if (threadCpuUsage.usageTimesMillis.length != cpuFrequencies.length) {
-                Slog.w(TAG, "Unexpected number of usage times,"
-                        + " expected " + cpuFrequencies.length
-                        + " but got " + threadCpuUsage.usageTimesMillis.length);
-                continue;
-            }
-
-            for (int i = 0; i < threadCpuUsage.usageTimesMillis.length; i++) {
-                // Do not report CPU usage at a frequency when it's zero
-                if (threadCpuUsage.usageTimesMillis[i] == 0) {
+        for (int i = 0; i < processCpuUsages.size(); i++) {
+            KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = processCpuUsages.get(i);
+            ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages =
+                    processCpuUsage.threadCpuUsages;
+            for (int j = 0; j < threadCpuUsages.size(); j++) {
+                KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage = threadCpuUsages.get(j);
+                if (threadCpuUsage.usageTimesMillis.length != cpuFrequencies.length) {
+                    Slog.w(TAG, "Unexpected number of usage times,"
+                            + " expected " + cpuFrequencies.length
+                            + " but got " + threadCpuUsage.usageTimesMillis.length);
                     continue;
                 }
 
-                StatsLogEventWrapper e =
-                        new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-                e.writeInt(processCpuUsage.uid);
-                e.writeInt(processCpuUsage.processId);
-                e.writeInt(threadCpuUsage.threadId);
-                e.writeString(processCpuUsage.processName);
-                e.writeString(threadCpuUsage.threadName);
-                e.writeInt(cpuFrequencies[i]);
-                e.writeInt(threadCpuUsage.usageTimesMillis[i]);
-                pulledData.add(e);
+                for (int k = 0; k < threadCpuUsage.usageTimesMillis.length; k++) {
+                    // Do not report CPU usage at a frequency when it's zero
+                    if (threadCpuUsage.usageTimesMillis[k] == 0) {
+                        continue;
+                    }
+
+                    StatsLogEventWrapper e =
+                            new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+                    e.writeInt(processCpuUsage.uid);
+                    e.writeInt(processCpuUsage.processId);
+                    e.writeInt(threadCpuUsage.threadId);
+                    e.writeString(processCpuUsage.processName);
+                    e.writeString(threadCpuUsage.threadName);
+                    e.writeInt(cpuFrequencies[k]);
+                    e.writeInt(threadCpuUsage.usageTimesMillis[k]);
+                    pulledData.add(e);
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 479f427..409d2b4 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -18,6 +18,7 @@
 
 import static android.app.WallpaperManager.FLAG_LOCK;
 import static android.app.WallpaperManager.FLAG_SYSTEM;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO;
 import static android.os.ParcelFileDescriptor.MODE_CREATE;
 import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
@@ -56,6 +57,7 @@
 import android.graphics.BitmapRegionDecoder;
 import android.graphics.Color;
 import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
@@ -118,6 +120,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
+import java.util.function.Consumer;
 
 public class WallpaperManagerService extends IWallpaperManager.Stub
         implements IWallpaperManagerService {
@@ -640,6 +643,43 @@
     final IPackageManager mIPackageManager;
     final MyPackageMonitor mMonitor;
     final AppOpsManager mAppOpsManager;
+
+    private final DisplayManager mDisplayManager;
+    private final DisplayManager.DisplayListener mDisplayListener =
+            new DisplayManager.DisplayListener() {
+
+        @Override
+        public void onDisplayAdded(int displayId) {
+            synchronized (mLock) {
+                if (mLastWallpaper != null) {
+                    final WallpaperConnection.DisplayConnector connector =
+                            mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
+                    if (connector == null) return;
+
+                    connector.connectLocked(mLastWallpaper.connection, mLastWallpaper);
+                }
+            }
+        }
+
+        @Override
+        public void onDisplayRemoved(int displayId) {
+            synchronized (mLock) {
+                if (mLastWallpaper != null) {
+                    final WallpaperConnection.DisplayConnector connector =
+                            mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
+                    if (connector == null) return;
+                    connector.disconnectLocked();
+                    mLastWallpaper.connection.removeDisplayConnector(displayId);
+                }
+            }
+        }
+
+        @Override
+        public void onDisplayChanged(int displayId) {
+            // TODO(b/115486823) Review that do we need to handle display changes.
+        }
+    };
+
     /**
      * Map of color listeners per user id.
      * The key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners.
@@ -775,20 +815,96 @@
     class WallpaperConnection extends IWallpaperConnection.Stub
             implements ServiceConnection {
 
+        /**
+         * Collect needed info for a display.
+         */
+        private final class DisplayConnector {
+            final int mDisplayId;
+            final Binder mToken = new Binder();
+            IWallpaperEngine mEngine;
+            boolean mDimensionsChanged;
+            boolean mPaddingChanged;
+
+            DisplayConnector(int displayId) {
+                mDisplayId = displayId;
+            }
+
+            void ensureStatusHandled() {
+                if (mDimensionsChanged) {
+                    try {
+                        mEngine.setDesiredSize(mWallpaper.width, mWallpaper.height);
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Failed to set wallpaper dimensions", e);
+                    }
+                    mDimensionsChanged = false;
+                }
+                if (mPaddingChanged) {
+                    try {
+                        mEngine.setDisplayPadding(mWallpaper.padding);
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Failed to set wallpaper padding", e);
+                    }
+                    mPaddingChanged = false;
+                }
+            }
+
+            void connectLocked(WallpaperConnection connection, WallpaperData wallpaper) {
+                if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
+                try {
+                    mIWindowManager.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed add wallpaper window token on display " + mDisplayId, e);
+                    return;
+                }
+
+                try {
+                    // TODO(b/115486823) Consider the size of non-default display
+                    connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
+                            wallpaper.width, wallpaper.height,
+                            wallpaper.padding, mDisplayId);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failed attaching wallpaper on display", e);
+                    // TODO(b/115486823) Failed when attaching a new engine, however, other engines
+                    // may still working. Should we abandon them all or just ignore this one.
+                    if (mLastWallpaper != null && !mLastWallpaper.wallpaperUpdating) {
+                        bindWallpaperComponentLocked(null /* componentName */, false /* force */,
+                                false /* fromUser */, wallpaper, null /* reply */);
+                    }
+                }
+            }
+
+            void disconnectLocked() {
+                if (DEBUG) Slog.v(TAG, "Removing window token: " + mToken);
+                try {
+                    mIWindowManager.removeWindowToken(mToken, mDisplayId);
+                } catch (RemoteException e) {
+                }
+                try {
+                    if (mEngine != null) {
+                        mEngine.destroy();
+                    }
+                } catch (RemoteException e) {
+                }
+                mEngine = null;
+            }
+        }
+
+        /**
+         * A map for each display.
+         * Use {@link #getDisplayConnectorOrCreate(int displayId)} to ensure the display is usable.
+         */
+        private SparseArray<DisplayConnector> mDisplayConnector = new SparseArray<>();
+
         /** Time in milliseconds until we expect the wallpaper to reconnect (unless we're in the
          *  middle of an update). If exceeded, the wallpaper gets reset to the system default. */
         private static final long WALLPAPER_RECONNECT_TIMEOUT_MS = 10000;
 
         final WallpaperInfo mInfo;
-        final Binder mToken = new Binder();
         IWallpaperService mService;
-        IWallpaperEngine mEngine;
         WallpaperData mWallpaper;
+        final int mClientUid;
         IRemoteCallback mReply;
 
-        boolean mDimensionsChanged = false;
-        boolean mPaddingChanged = false;
-
         private Runnable mResetRunnable = () -> {
             synchronized (mLock) {
                 if (mShuttingDown) {
@@ -809,9 +925,55 @@
             }
         };
 
-        public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) {
+        WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper, int clientUid) {
             mInfo = info;
             mWallpaper = wallpaper;
+            mClientUid = clientUid;
+            initDisplayState();
+        }
+
+        private void initDisplayState() {
+            final Display[] displays = mDisplayManager.getDisplays();
+            for (Display display : displays) {
+                if (isUsableDisplay(display)) {
+                    final int displayId = display.getDisplayId();
+                    mDisplayConnector.append(displayId, new DisplayConnector(displayId));
+                }
+            }
+        }
+
+        // TODO(b/115486823) Support the system decorations change at runtime.
+        private boolean isUsableDisplay(Display display) {
+            return display != null &&  display.hasAccess(mClientUid)
+                    // TODO(b/114338689) Use WindowManager.supportsSystemDecorations when ready
+                    && (display.supportsSystemDecorations()
+                            || display.getDisplayId() == DEFAULT_DISPLAY);
+        }
+
+        void forEachDisplayConnector(Consumer<DisplayConnector> action) {
+            for (int i = mDisplayConnector.size() - 1; i >= 0; i--) {
+                final DisplayConnector connector = mDisplayConnector.get(i);
+                action.accept(connector);
+            }
+        }
+
+        DisplayConnector getDisplayConnectorOrCreate(int displayId) {
+            DisplayConnector connector = mDisplayConnector.get(displayId);
+            if (connector == null) {
+                final Display display = mDisplayManager.getDisplay(displayId);
+                if (isUsableDisplay(display)) {
+                    connector = new DisplayConnector(displayId);
+                    mDisplayConnector.append(displayId, connector);
+                }
+            }
+            return connector;
+        }
+
+        void removeDisplayConnector(int displayId) {
+            final DisplayConnector connector = mDisplayConnector.get(displayId);
+            if (connector != null) {
+                mDisplayConnector.remove(displayId);
+            }
         }
 
         @Override
@@ -839,7 +1001,7 @@
                             + mWallpaper.wallpaperComponent);
                 }
                 mService = null;
-                mEngine = null;
+                forEachDisplayConnector(connector -> connector.mEngine = null);
                 if (mWallpaper.connection == this) {
                     // There is an inherent ordering race between this callback and the
                     // package monitor that receives notice that a package is being updated,
@@ -863,7 +1025,8 @@
             fgHandler.removeCallbacks(mResetRunnable);
             fgHandler.postDelayed(mResetRunnable, WALLPAPER_RECONNECT_TIMEOUT_MS);
             if (DEBUG_LIVE) {
-                Slog.i(TAG, "Started wallpaper reconnect timeout for " + mWallpaper.wallpaperComponent);
+                Slog.i(TAG,
+                        "Started wallpaper reconnect timeout for " + mWallpaper.wallpaperComponent);
             }
         }
 
@@ -943,38 +1106,37 @@
         }
 
         @Override
-        public void attachEngine(IWallpaperEngine engine) {
+        public void attachEngine(IWallpaperEngine engine, int displayId) {
             synchronized (mLock) {
-                mEngine = engine;
-                if (mDimensionsChanged) {
+                final DisplayConnector connector = getDisplayConnectorOrCreate(displayId);
+                if (connector == null) {
                     try {
-                        mEngine.setDesiredSize(mWallpaper.width, mWallpaper.height);
+                        engine.destroy();
                     } catch (RemoteException e) {
-                        Slog.w(TAG, "Failed to set wallpaper dimensions", e);
+                        Slog.w(TAG, "Failed to destroy engine", e);
                     }
-                    mDimensionsChanged = false;
+                    return;
                 }
-                if (mPaddingChanged) {
+                connector.mEngine = engine;
+                connector.ensureStatusHandled();
+
+                // TODO(multi-display) TBD.
+                if (mInfo != null && mInfo.supportsAmbientMode() && displayId == DEFAULT_DISPLAY) {
                     try {
-                        mEngine.setDisplayPadding(mWallpaper.padding);
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "Failed to set wallpaper padding", e);
-                    }
-                    mPaddingChanged = false;
-                }
-                if (mInfo != null && mInfo.supportsAmbientMode()) {
-                    try {
-                        mEngine.setInAmbientMode(mInAmbientMode, false /* animated */);
+                        connector.mEngine.setInAmbientMode(mInAmbientMode, false /* animated */);
                     } catch (RemoteException e) {
                         Slog.w(TAG, "Failed to set ambient mode state", e);
                     }
                 }
-                try {
-                    // This will trigger onComputeColors in the wallpaper engine.
-                    // It's fine to be locked in here since the binder is oneway.
-                    mEngine.requestWallpaperColors();
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Failed to request wallpaper colors", e);
+                // TODO(b/115486823) Extends for secondary display.
+                if (displayId == DEFAULT_DISPLAY) {
+                    try {
+                        // This will trigger onComputeColors in the wallpaper engine.
+                        // It's fine to be locked in here since the binder is oneway.
+                        connector.mEngine.requestWallpaperColors();
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Failed to request wallpaper colors", e);
+                    }
                 }
             }
         }
@@ -1162,6 +1324,8 @@
                 ServiceManager.getService(Context.WINDOW_SERVICE));
         mIPackageManager = AppGlobals.getPackageManager();
         mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        mDisplayManager = mContext.getSystemService(DisplayManager.class);
+        mDisplayManager.registerDisplayListener(mDisplayListener, null /* handler */);
         mMonitor = new MyPackageMonitor();
         mColorsChangedListeners = new SparseArray<>();
     }
@@ -1541,6 +1705,7 @@
         return false;
     }
 
+    // TODO(b/115486823) Extends this method with specific display.
     public void setDimensionHints(int width, int height, String callingPackage)
             throws RemoteException {
         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
@@ -1560,10 +1725,12 @@
                 saveSettingsLocked(userId);
                 if (mCurrentUserId != userId) return; // Don't change the properties now
                 if (wallpaper.connection != null) {
-                    if (wallpaper.connection.mEngine != null) {
+                    // TODO(b/115486823) Extends this method with specific display.
+                    final IWallpaperEngine engine = wallpaper.connection
+                            .getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
+                    if (engine != null) {
                         try {
-                            wallpaper.connection.mEngine.setDesiredSize(
-                                    width, height);
+                            engine.setDesiredSize(width, height);
                         } catch (RemoteException e) {
                         }
                         notifyCallbacksLocked(wallpaper);
@@ -1571,7 +1738,9 @@
                         // We've attached to the service but the engine hasn't attached back to us
                         // yet. This means it will be created with the previous dimensions, so we
                         // need to update it to the new dimensions once it attaches.
-                        wallpaper.connection.mDimensionsChanged = true;
+                        // TODO(b/115486823) Extends this method with specific display.
+                        wallpaper.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY)
+                                .mDimensionsChanged = true;
                     }
                 }
             }
@@ -1600,6 +1769,7 @@
         }
     }
 
+    // TODO(b/115486823) Extends this method with specific display.
     public void setDisplayPadding(Rect padding, String callingPackage) {
         checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
         if (!isWallpaperSupported(callingPackage)) {
@@ -1617,9 +1787,12 @@
                 saveSettingsLocked(userId);
                 if (mCurrentUserId != userId) return; // Don't change the properties now
                 if (wallpaper.connection != null) {
-                    if (wallpaper.connection.mEngine != null) {
+                    // TODO(b/115486823) Extends this method with specific display.
+                    final IWallpaperEngine engine = wallpaper.connection
+                            .getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
+                    if (engine != null) {
                         try {
-                            wallpaper.connection.mEngine.setDisplayPadding(padding);
+                            engine.setDisplayPadding(padding);
                         } catch (RemoteException e) {
                         }
                         notifyCallbacksLocked(wallpaper);
@@ -1627,7 +1800,9 @@
                         // We've attached to the service but the engine hasn't attached back to us
                         // yet. This means it will be created with the previous dimensions, so we
                         // need to update it to the new dimensions once it attaches.
-                        wallpaper.connection.mPaddingChanged = true;
+                        // TODO(b/115486823) Extends this method with specific display.
+                        wallpaper.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY)
+                                .mPaddingChanged = true;
                     }
                 }
             }
@@ -1756,6 +1931,7 @@
         }
     }
 
+    // TODO(b/115486823) Extends this method with specific display.
     public void setInAmbientMode(boolean inAmbienMode, boolean animated) {
         final IWallpaperEngine engine;
         synchronized (mLock) {
@@ -1763,7 +1939,8 @@
             final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
             if (data != null && data.connection != null && data.connection.mInfo != null
                     && data.connection.mInfo.supportsAmbientMode()) {
-                engine = data.connection.mEngine;
+                // TODO(b/115486823) Extends this method with specific display.
+                engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
             } else {
                 engine = null;
             }
@@ -2125,7 +2302,9 @@
 
             // Bind the service!
             if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
-            WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);
+            final int componentUid = mIPackageManager.getPackageUid(componentName.getPackageName(),
+                    MATCH_DIRECT_BOOT_AUTO, wallpaper.userId);
+            WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper, componentUid);
             intent.setComponent(componentName);
             intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
                     com.android.internal.R.string.wallpaper_binding_label);
@@ -2152,14 +2331,8 @@
             wallpaper.wallpaperComponent = componentName;
             wallpaper.connection = newConn;
             newConn.mReply = reply;
-            try {
-                if (wallpaper.userId == mCurrentUserId) {
-                    if (DEBUG)
-                        Slog.v(TAG, "Adding window token: " + newConn.mToken);
-                    mIWindowManager.addWindowToken(newConn.mToken, TYPE_WALLPAPER, DEFAULT_DISPLAY);
-                    mLastWallpaper = wallpaper;
-                }
-            } catch (RemoteException e) {
+            if (wallpaper.userId == mCurrentUserId) {
+                mLastWallpaper = wallpaper;
             }
         } catch (RemoteException e) {
             String msg = "Remote exception for " + componentName + "\n" + e;
@@ -2181,22 +2354,12 @@
                 }
                 wallpaper.connection.mReply = null;
             }
-            if (wallpaper.connection.mEngine != null) {
-                try {
-                    wallpaper.connection.mEngine.destroy();
-                } catch (RemoteException e) {
-                }
-            }
             mContext.unbindService(wallpaper.connection);
-            try {
-                if (DEBUG)
-                    Slog.v(TAG, "Removing window token: " + wallpaper.connection.mToken);
-                mIWindowManager.removeWindowToken(wallpaper.connection.mToken, DEFAULT_DISPLAY);
-            } catch (RemoteException e) {
-            }
+            wallpaper.connection.forEachDisplayConnector(connector -> connector.disconnectLocked());
             wallpaper.connection.mService = null;
-            wallpaper.connection.mEngine = null;
+            wallpaper.connection.mDisplayConnector.clear();
             wallpaper.connection = null;
+            if (wallpaper == mLastWallpaper) mLastWallpaper = null;
         }
     }
 
@@ -2206,16 +2369,7 @@
     }
 
     void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
-        try {
-            conn.mService.attach(conn, conn.mToken,
-                    TYPE_WALLPAPER, false,
-                    wallpaper.width, wallpaper.height, wallpaper.padding);
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
-            if (!wallpaper.wallpaperUpdating) {
-                bindWallpaperComponentLocked(null, false, false, wallpaper, null);
-            }
-        }
+        conn.forEachDisplayConnector(connector-> connector.connectLocked(conn, wallpaper));
     }
 
     private void notifyCallbacksLocked(WallpaperData wallpaper) {
@@ -2801,12 +2955,16 @@
                         pw.print("    mInfo.component=");
                         pw.println(conn.mInfo.getComponent());
                     }
-                    pw.print("    mToken=");
-                    pw.println(conn.mToken);
+                    conn.forEachDisplayConnector(connector -> {
+                        pw.print("    mDisplayId=");
+                        pw.println(connector.mDisplayId);
+                        pw.print("    mToken=");
+                        pw.println(connector.mToken);
+                        pw.print("    mEngine=");
+                        pw.println(connector.mEngine);
+                    });
                     pw.print("    mService=");
                     pw.println(conn.mService);
-                    pw.print("    mEngine=");
-                    pw.println(conn.mEngine);
                     pw.print("    mLastDiedTime=");
                     pw.println(wallpaper.lastDiedTime - SystemClock.uptimeMillis());
                 }
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 94a47dd..e65f3b4 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -51,7 +51,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
 
-import android.app.WindowConfiguration;
 import android.os.Trace;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -83,7 +82,7 @@
     AppTransitionController(WindowManagerService service, DisplayContent displayContent) {
         mService = service;
         mDisplayContent = displayContent;
-        mWallpaperControllerLocked = new WallpaperController(mService);
+        mWallpaperControllerLocked = mDisplayContent.mWallpaperController;
     }
 
     /**
@@ -106,7 +105,7 @@
 
         mDisplayContent.mAppTransition.removeAppTransitionTimeoutCallbacks();
 
-        mService.mRoot.mWallpaperMayChange = false;
+        mDisplayContent.mWallpaperMayChange = false;
 
         int i;
         for (i = 0; i < appsCount; i++) {
@@ -121,7 +120,7 @@
         // Adjust wallpaper before we pull the lower/upper target, since pending changes
         // (like the clearAnimatingFlags() above) might affect wallpaper target result.
         // Or, the opening app window should be a wallpaper target.
-        mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(mDisplayContent,
+        mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
                 mDisplayContent.mOpeningApps);
 
         // Determine if closing and opening app token sets are wallpaper targets, in which case
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ab7d259..2f7956e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -414,6 +414,8 @@
 
     WallpaperController mWallpaperController;
 
+    boolean mWallpaperMayChange = false;
+
     private final SurfaceSession mSession = new SurfaceSession();
 
     /**
@@ -751,8 +753,7 @@
             }
         }
 
-        if (isDefaultDisplay && obscuredChanged && w.isVisibleLw()
-                && mWallpaperController.isWallpaperTarget(w)) {
+        if (obscuredChanged && w.isVisibleLw() && mWallpaperController.isWallpaperTarget(w)) {
             // This is the wallpaper target and its obscured state changed... make sure the
             // current wallpaper's visibility has been updated accordingly.
             mWallpaperController.updateWallpaperVisibility();
@@ -784,7 +785,7 @@
                 if ((w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
                     if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
                             "First draw done in potential wallpaper target " + w);
-                    root.mWallpaperMayChange = true;
+                    mWallpaperMayChange = true;
                     pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                     if (DEBUG_LAYOUT_REPEATS) {
                         surfacePlacer.debugLayoutRepeats(
@@ -816,11 +817,10 @@
      * initialize direct children.
      * @param display May not be null.
      * @param service You know.
-     * @param wallpaperController wallpaper windows controller used to adjust the positioning of the
-     *                            wallpaper windows in the window list.
+     * @param controller The controller for the display container.
      */
     DisplayContent(Display display, WindowManagerService service,
-            WallpaperController wallpaperController, DisplayWindowController controller) {
+            DisplayWindowController controller) {
         super(service);
         setController(controller);
         if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
@@ -831,7 +831,7 @@
 
         mDisplay = display;
         mDisplayId = display.getDisplayId();
-        mWallpaperController = wallpaperController;
+        mWallpaperController = new WallpaperController(mService, this);
         display.getDisplayInfo(mDisplayInfo);
         display.getMetrics(mDisplayMetrics);
         isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
@@ -2661,6 +2661,9 @@
         pw.print("  mFocusedApp="); pw.println(mFocusedApp);
 
         pw.println();
+        mWallpaperController.dump(pw, "  ");
+
+        pw.println();
         pw.println(prefix + "Application tokens in top down Z order:");
         for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
             final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
@@ -3388,12 +3391,8 @@
             if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("On entry to LockedInner",
                     pendingLayoutChanges);
 
-            // TODO(multi-display): For now adjusting wallpaper only on primary display to avoid
-            // the wallpaper window jumping across displays.
-            // Remove check for default display when there will be support for multiple wallpaper
-            // targets (on different displays).
-            if (isDefaultDisplay && (pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
-                mWallpaperController.adjustWallpaperWindows(this);
+            if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
+                mWallpaperController.adjustWallpaperWindows();
             }
 
             if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
@@ -4673,7 +4672,7 @@
             Slog.v(TAG_WM, "Wallpaper layer changed: assigning layers + relayout");
         }
         computeImeTarget(true /* updateImeTarget */);
-        mService.mRoot.mWallpaperMayChange = true;
+        mWallpaperMayChange = true;
         // Since the window list has been rebuilt, focus might have to be recomputed since the
         // actual order of windows might have changed again.
         mService.mFocusMayChange = true;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 61bc4e4..83d32c8ad 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -40,14 +40,11 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
+import android.view.InputApplicationHandle;
 import android.view.InputChannel;
 import android.view.InputEventReceiver;
-import android.view.KeyEvent;
-import android.view.WindowManager;
-import android.view.InputApplicationHandle;
 import android.view.InputWindowHandle;
 
-import com.android.server.input.InputManagerService;
 import com.android.server.policy.WindowManagerPolicy;
 
 import java.io.PrintWriter;
@@ -401,10 +398,10 @@
             mTmpRect.setEmpty();
             mDisableWallpaperTouchEvents = false;
             this.inDrag = inDrag;
-            wallpaperController = mService.mRoot.mWallpaperController;
+            final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
+            wallpaperController = dc.mWallpaperController;
 
-            mService.mRoot.getDisplayContent(mDisplayId).forAllWindows(this,
-                    true /* traverseTopToBottom */);
+            dc.forAllWindows(this, true /* traverseTopToBottom */);
             if (mAddWallpaperInputConsumerHandle) {
                 // No visible wallpaper found, add the wallpaper input consumer at the end.
                 addInputWindowHandle(wallpaperInputConsumer.mWindowHandle);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2399716..72e3562 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -51,7 +51,6 @@
 import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
 import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
 import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
-import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
 
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
@@ -106,7 +105,6 @@
     private boolean mSustainedPerformanceModeEnabled = false;
     private boolean mSustainedPerformanceModeCurrent = false;
 
-    boolean mWallpaperMayChange = false;
     // During an orientation change, we track whether all windows have rendered
     // at the new orientation, and this will be false from changing orientation until that occurs.
     // For seamless rotation cases this always stays true, as the windows complete their orientation
@@ -114,8 +112,6 @@
     boolean mOrientationChangeComplete = true;
     boolean mWallpaperActionPending = false;
 
-    final WallpaperController mWallpaperController;
-
     private final Handler mHandler;
 
     private String mCloseSystemDialogsReason;
@@ -150,7 +146,6 @@
     RootWindowContainer(WindowManagerService service) {
         super(service);
         mHandler = new MyHandler(service.mH.getLooper());
-        mWallpaperController = new WallpaperController(mService);
     }
 
     boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
@@ -236,8 +231,7 @@
             return existing;
         }
 
-        final DisplayContent dc =
-                new DisplayContent(display, mService, mWallpaperController, controller);
+        final DisplayContent dc = new DisplayContent(display, mService, controller);
 
         if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
 
@@ -579,14 +573,19 @@
         final RecentsAnimationController recentsAnimationController =
                 mService.getRecentsAnimationController();
         if (recentsAnimationController != null) {
-            recentsAnimationController.checkAnimationReady(mWallpaperController);
+            recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
         }
 
-        if (mWallpaperMayChange) {
-            if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change!  Adjusting");
-            defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
-            if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("WallpaperMayChange",
-                    defaultDisplay.pendingLayoutChanges);
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final DisplayContent displayContent = mChildren.get(displayNdx);
+            if (displayContent.mWallpaperMayChange) {
+                if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change!  Adjusting");
+                displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+                if (DEBUG_LAYOUT_REPEATS) {
+                    surfacePlacer.debugLayoutRepeats("WallpaperMayChange",
+                            displayContent.pendingLayoutChanges);
+                }
+            }
         }
 
         if (mService.mFocusMayChange) {
@@ -617,7 +616,6 @@
         }
 
         // Destroy the surface of any windows that are no longer visible.
-        boolean wallpaperDestroyed = false;
         i = mService.mDestroySurface.size();
         if (i > 0) {
             do {
@@ -629,7 +627,7 @@
                     displayContent.setInputMethodWindowLocked(null);
                 }
                 if (displayContent.mWallpaperController.isWallpaperTarget(win)) {
-                    wallpaperDestroyed = true;
+                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                 }
                 win.destroySurfaceUnchecked();
                 win.mWinAnimator.destroyPreservedSurfaceLocked();
@@ -643,11 +641,6 @@
             displayContent.removeExistingTokensIfPossible();
         }
 
-        if (wallpaperDestroyed) {
-            defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
-            defaultDisplay.setLayoutNeeded();
-        }
-
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
             final DisplayContent displayContent = mChildren.get(displayNdx);
             if (displayContent.pendingLayoutChanges != 0) {
@@ -905,10 +898,6 @@
             mUpdateRotation = true;
             doRequest = true;
         }
-        if ((bulkUpdateParams & SET_WALLPAPER_MAY_CHANGE) != 0) {
-            mWallpaperMayChange = true;
-            doRequest = true;
-        }
         if ((bulkUpdateParams & SET_ORIENTATION_CHANGE_COMPLETE) == 0) {
             mOrientationChangeComplete = false;
         } else {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b411fad..6838c55 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -29,7 +29,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.content.ClipData;
-import android.content.Context;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Binder;
@@ -42,7 +41,6 @@
 import android.os.UserHandle;
 import android.util.MergedConfiguration;
 import android.util.Slog;
-import android.view.Display;
 import android.view.DisplayCutout;
 import android.view.IWindow;
 import android.view.IWindowId;
@@ -60,6 +58,7 @@
 import java.io.PrintWriter;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.function.BiConsumer;
 
 /**
  * This class represents an active client session.  There is generally one
@@ -315,14 +314,19 @@
         }
     }
 
+    private void actionOnWallpaper(IBinder window,
+            BiConsumer<WallpaperController, WindowState> action) {
+        final WindowState windowState = mService.windowForClientLocked(this, window, true);
+        action.accept(windowState.getDisplayContent().mWallpaperController, windowState);
+    }
+
     @Override
     public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
         synchronized (mService.mGlobalLock) {
             long ident = Binder.clearCallingIdentity();
             try {
-                mService.mRoot.mWallpaperController.setWindowWallpaperPosition(
-                        mService.windowForClientLocked(this, window, true),
-                        x, y, xStep, yStep);
+                actionOnWallpaper(window, (wpController, windowState) ->
+                        wpController.setWindowWallpaperPosition(windowState, x, y, xStep, yStep));
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -332,7 +336,8 @@
     @Override
     public void wallpaperOffsetsComplete(IBinder window) {
         synchronized (mService.mGlobalLock) {
-            mService.mRoot.mWallpaperController.wallpaperOffsetsComplete(window);
+            actionOnWallpaper(window, (wpController, windowState) ->
+                    wpController.wallpaperOffsetsComplete(window));
         }
     }
 
@@ -341,8 +346,8 @@
         synchronized (mService.mGlobalLock) {
             long ident = Binder.clearCallingIdentity();
             try {
-                mService.mRoot.mWallpaperController.setWindowWallpaperDisplayOffset(
-                        mService.windowForClientLocked(this, window, true), x, y);
+                actionOnWallpaper(window, (wpController, windowState) ->
+                        wpController.setWindowWallpaperDisplayOffset(windowState, x, y));
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -355,9 +360,9 @@
         synchronized (mService.mGlobalLock) {
             long ident = Binder.clearCallingIdentity();
             try {
-                return mService.mRoot.mWallpaperController.sendWindowWallpaperCommand(
-                        mService.windowForClientLocked(this, window, true),
-                        action, x, y, z, extras, sync);
+                final WindowState windowState = mService.windowForClientLocked(this, window, true);
+                return windowState.getDisplayContent().mWallpaperController
+                        .sendWindowWallpaperCommand(windowState, action, x, y, z, extras, sync);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -367,7 +372,8 @@
     @Override
     public void wallpaperCommandComplete(IBinder window, Bundle result) {
         synchronized (mService.mGlobalLock) {
-            mService.mRoot.mWallpaperController.wallpaperCommandComplete(window);
+            actionOnWallpaper(window, (wpController, windowState) ->
+                    wpController.wallpaperCommandComplete(window));
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 29e1b20..15239c7 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -60,6 +60,7 @@
 class WallpaperController {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "WallpaperController" : TAG_WM;
     private WindowManagerService mService;
+    private final DisplayContent mDisplayContent;
 
     private final ArrayList<WallpaperWindowToken> mWallpaperTokens = new ArrayList<>();
 
@@ -187,8 +188,9 @@
         return false;
     };
 
-    public WallpaperController(WindowManagerService service) {
+    WallpaperController(WindowManagerService service, DisplayContent displayContent) {
         mService = service;
+        mDisplayContent = displayContent;
     }
 
     WindowState getWallpaperTarget() {
@@ -397,11 +399,7 @@
     }
 
     private void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) {
-        final DisplayContent displayContent = changingTarget.getDisplayContent();
-        if (displayContent == null) {
-            return;
-        }
-        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+        final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
         final int dw = displayInfo.logicalWidth;
         final int dh = displayInfo.logicalHeight;
 
@@ -464,15 +462,15 @@
         }
     }
 
-    private void findWallpaperTarget(DisplayContent dc) {
+    private void findWallpaperTarget() {
         mFindResults.reset();
-        if (dc.isStackVisible(WINDOWING_MODE_FREEFORM)) {
+        if (mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM)) {
             // In freeform mode we set the wallpaper as its own target, so we don't need an
             // additional window to make it visible.
             mFindResults.setUseTopWallpaperAsTarget(true);
         }
 
-        dc.forAllWindows(mFindWallpaperTargetFunction, true /* traverseTopToBottom */);
+        mDisplayContent.forAllWindows(mFindWallpaperTargetFunction, true /* traverseTopToBottom */);
 
         if (mFindResults.wallpaperTarget == null && mFindResults.useTopWallpaperAsTarget) {
             mFindResults.setWallpaperTarget(mFindResults.topWallpaper);
@@ -485,8 +483,7 @@
     }
 
     /** Updates the target wallpaper if needed and returns true if an update happened. */
-    private void updateWallpaperWindowsTarget(DisplayContent dc,
-            FindWallpaperTargetResult result) {
+    private void updateWallpaperWindowsTarget(FindWallpaperTargetResult result) {
 
         WindowState wallpaperTarget = result.wallpaperTarget;
 
@@ -529,7 +526,7 @@
             return;
         }
 
-        if (dc.getWindow(w -> w == prevWallpaperTarget) == null) {
+        if (mDisplayContent.getWindow(w -> w == prevWallpaperTarget) == null) {
             return;
         }
 
@@ -550,9 +547,9 @@
             // is not. If they're both hidden, still use the new target.
             mWallpaperTarget = prevWallpaperTarget;
         } else if (newTargetHidden == oldTargetHidden
-                && !dc.mOpeningApps.contains(wallpaperTarget.mAppToken)
-                && (dc.mOpeningApps.contains(prevWallpaperTarget.mAppToken)
-                || dc.mClosingApps.contains(prevWallpaperTarget.mAppToken))) {
+                && !mDisplayContent.mOpeningApps.contains(wallpaperTarget.mAppToken)
+                && (mDisplayContent.mOpeningApps.contains(prevWallpaperTarget.mAppToken)
+                || mDisplayContent.mClosingApps.contains(prevWallpaperTarget.mAppToken))) {
             // If they're both hidden (or both not hidden), prefer the one that's currently in
             // opening or closing app list, this allows transition selection logic to better
             // determine the wallpaper status of opening/closing apps.
@@ -570,18 +567,21 @@
         }
     }
 
-    void adjustWallpaperWindows(DisplayContent dc) {
-        mService.mRoot.mWallpaperMayChange = false;
+    void adjustWallpaperWindows() {
+        mDisplayContent.mWallpaperMayChange = false;
 
         // First find top-most window that has asked to be on top of the wallpaper;
         // all wallpapers go behind it.
-        findWallpaperTarget(dc);
-        updateWallpaperWindowsTarget(dc, mFindResults);
+        findWallpaperTarget();
+        updateWallpaperWindowsTarget(mFindResults);
 
         // The window is visible to the compositor...but is it visible to the user?
         // That is what the wallpaper cares about.
         final boolean visible = mWallpaperTarget != null && isWallpaperVisible(mWallpaperTarget);
-        if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper visibility: " + visible);
+        if (DEBUG_WALLPAPER) {
+            Slog.v(TAG, "Wallpaper visibility: " + visible + " at display "
+                    + mDisplayContent.getDisplayId());
+        }
 
         if (visible) {
             if (mWallpaperTarget.mWallpaperX >= 0) {
@@ -637,9 +637,11 @@
                 }
                 if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) {
                     mWallpaperDrawState = WALLPAPER_DRAW_PENDING;
-                    mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT);
-                    mService.mH.sendEmptyMessageDelayed(WALLPAPER_DRAW_PENDING_TIMEOUT,
-                            WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION);
+                    mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT, this);
+                    mService.mH.sendMessageDelayed(
+                                mService.mH.obtainMessage(WALLPAPER_DRAW_PENDING_TIMEOUT, this),
+                                WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION);
+
                 }
                 if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
                         "Wallpaper should be visible but has not been drawn yet. " +
@@ -649,7 +651,7 @@
         }
         if (wallpaperReady) {
             mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
-            mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT);
+            mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT, this);
         }
 
         return transitionReady;
@@ -659,10 +661,9 @@
      * Adjusts the wallpaper windows if the input display has a pending wallpaper layout or one of
      * the opening apps should be a wallpaper target.
      */
-    void adjustWallpaperWindowsForAppTransitionIfNeeded(DisplayContent dc,
-            ArraySet<AppWindowToken> openingApps) {
+    void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<AppWindowToken> openingApps) {
         boolean adjust = false;
-        if ((dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
+        if ((mDisplayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
             adjust = true;
         } else {
             for (int i = openingApps.size() - 1; i >= 0; --i) {
@@ -675,7 +676,7 @@
         }
 
         if (adjust) {
-            adjustWallpaperWindows(dc);
+            adjustWallpaperWindows();
         }
     }
 
@@ -740,6 +741,7 @@
     }
 
     void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix); pw.print("displayId="); pw.println(mDisplayContent.getDisplayId());
         pw.print(prefix); pw.print("mWallpaperTarget="); pw.println(mWallpaperTarget);
         if (mPrevWallpaperTarget != null) {
             pw.print(prefix); pw.print("mPrevWallpaperTarget="); pw.println(mPrevWallpaperTarget);
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 46bb981..449c409 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.view.Display.DEFAULT_DISPLAY;
+
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -270,9 +271,6 @@
         if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) {
             builder.append(" UPDATE_ROTATION");
         }
-        if ((bulkUpdateParams & WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE) != 0) {
-            builder.append(" WALLPAPER_MAY_CHANGE");
-        }
         if ((bulkUpdateParams & WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE) != 0) {
             builder.append(" ORIENTATION_CHANGE_COMPLETE");
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c47b22f..8d5cd78 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2286,7 +2286,7 @@
         }
 
         synchronized (mGlobalLock) {
-            final DisplayContent dc = mRoot.getDisplayContent(displayId);
+            final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */);
             if (dc == null) {
                 Slog.w(TAG_WM, "addWindowToken: Attempted to add token: " + binder
                         + " for non-exiting displayId=" + displayId);
@@ -3462,7 +3462,9 @@
         try {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper");
             synchronized (mGlobalLock) {
-                return mRoot.mWallpaperController.screenshotWallpaperLocked();
+                // TODO(b/115486823) Screenshot at secondary displays if needed.
+                final DisplayContent dc = mRoot.getDisplayContent(DEFAULT_DISPLAY);
+                return dc.mWallpaperController.screenshotWallpaperLocked();
             }
         } finally {
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -4694,7 +4696,10 @@
                 break;
                 case WALLPAPER_DRAW_PENDING_TIMEOUT: {
                     synchronized (mGlobalLock) {
-                        if (mRoot.mWallpaperController.processWallpaperDrawPendingTimeout()) {
+                        final WallpaperController wallpaperController =
+                                (WallpaperController) msg.obj;
+                        if (wallpaperController != null
+                                && wallpaperController.processWallpaperDrawPendingTimeout()) {
                             mWindowPlacerLocked.performSurfacePlacement();
                         }
                     }
@@ -5975,7 +5980,6 @@
                 pw.print("  mInputMethodWindow="); pw.println(imeWindow);
             }
             mWindowPlacerLocked.dump(pw, "  ");
-            mRoot.mWallpaperController.dump(pw, "  ");
             pw.print("  mSystemBooted="); pw.print(mSystemBooted);
                     pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
 
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 838d2a1..15ea5b5 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -245,7 +245,7 @@
         mSession = win.mSession;
         mAttrType = win.mAttrs.type;
         mIsWallpaper = win.mIsWallpaper;
-        mWallpaperControllerLocked = mService.mRoot.mWallpaperController;
+        mWallpaperControllerLocked = win.getDisplayContent().mWallpaperController;
     }
 
     void cancelExitAnimationForNextAnimationLocked() {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 7d25b8c..7193dd7 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -38,7 +38,6 @@
 class WindowSurfacePlacer {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowSurfacePlacer" : TAG_WM;
     private final WindowManagerService mService;
-    private final WallpaperController mWallpaperControllerLocked;
 
     private boolean mInLayout = false;
 
@@ -46,7 +45,6 @@
     private int mLayoutRepeatCount;
 
     static final int SET_UPDATE_ROTATION                = 1 << 0;
-    static final int SET_WALLPAPER_MAY_CHANGE           = 1 << 1;
     static final int SET_ORIENTATION_CHANGE_COMPLETE    = 1 << 2;
     static final int SET_WALLPAPER_ACTION_PENDING       = 1 << 3;
 
@@ -59,7 +57,6 @@
 
     public WindowSurfacePlacer(WindowManagerService service) {
         mService = service;
-        mWallpaperControllerLocked = mService.mRoot.mWallpaperController;
         mPerformSurfacePlacement = () -> {
             synchronized (mService.mGlobalLock) {
                 performSurfacePlacement();
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 04a526f..bf83ac13 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -83,6 +83,7 @@
         "libui",
         "libinput",
         "libinputflinger",
+        "libinputflinger_base",
         "libinputservice",
         "libschedulerservicehidl",
         "libsensorservice",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index e2db807..ee8a08b 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -341,8 +341,7 @@
     }
     mInteractive = true;
 
-    sp<EventHub> eventHub = new EventHub();
-    mInputManager = new InputManager(eventHub, this, this);
+    mInputManager = new InputManager(this, this);
 }
 
 NativeInputManager::~NativeInputManager() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 2dbbf55..5926bdd 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -88,4 +88,7 @@
     public String getGlobalPrivateDnsHost(ComponentName who) {
         return null;
     }
+
+    @Override
+    public void grantDeviceIdsAccessToProfileOwner(ComponentName who, int userId) { }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a7542d7..bbbc40c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -55,16 +55,15 @@
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
-import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_UNKNOWN;
 import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_OFF;
 import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
 import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_UNKNOWN;
 import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
 import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
 import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
 import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
-
 import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
 import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
 import static android.provider.Telephony.Carriers.DPC_URI;
@@ -75,11 +74,8 @@
         .PROVISIONING_ENTRY_POINT_ADB;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
         .STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
-
 import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
 import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
-
-
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -228,19 +224,19 @@
 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.internal.util.JournaledFile;
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.StatLogger;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.LocalServices;
 import com.android.server.LockGuard;
-import com.android.internal.util.StatLogger;
 import com.android.server.SystemServerInitThreadPool;
 import com.android.server.SystemService;
 import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.pm.UserRestrictionsUtils;
 import com.android.server.storage.DeviceStorageMonitorInternal;
-
 import com.android.server.uri.UriGrantsManagerInternal;
+
 import com.google.android.collect.Sets;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -267,7 +263,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -2701,7 +2696,7 @@
         final DevicePolicyData policy = getUserData(userId);
         ActiveAdmin admin = policy.mAdminMap.get(who);
         if (admin == null) {
-            throw new SecurityException("No active admin " + who);
+            throw new SecurityException("No active admin " + who + " for UID " + uid);
         }
         if (admin.getUid() != uid) {
             throw new SecurityException("Admin " + who + " is not owned by uid " + uid);
@@ -2709,6 +2704,16 @@
         return admin;
     }
 
+    /**
+     * Returns the active admin for the user of the caller as denoted by uid, which implements
+     * the {@code reqPolicy}.
+     *
+     * The {@code who} parameter is used as a hint:
+     * If provided, it must be the component name of the active admin for that user and the caller
+     * uid must match the uid of the admin.
+     * If not provided, iterate over all of the active admins in the DevicePolicyData for that user
+     * and return the one with the uid specified as parameter, and has the policy specified.
+     */
     private ActiveAdmin getActiveAdminWithPolicyForUidLocked(ComponentName who, int reqPolicy,
             int uid) {
         ensureLocked();
@@ -5435,23 +5440,54 @@
         return false;
     }
 
-    private void enforceIsDeviceOwnerOrCertInstallerOfDeviceOwner(
+    /**
+     * Enforce one the following conditions are met:
+     * (1) The device has a Device Owner, and one of the following holds:
+     *   (1.1) The caller is the Device Owner
+     *   (1.2) The caller is another app in the same user as the device owner, AND
+     *         The caller is the delegated certificate installer.
+     * (2) The user has a profile owner, AND:
+     *   (2.1) The profile owner has been granted access to Device IDs and one of the following
+     *         holds:
+     *     (2.1.1) The caller is the profile owner.
+     *     (2.1.2) The caller is from another app in the same user as the profile owner, AND
+     *       (2.1.2.1) The caller is the delegated cert installer.
+     *
+     *  For the device owner case, simply check that the caller is the device owner or the
+     *  delegated certificate installer.
+     *
+     *  For the profile owner case, first check that the caller is the profile owner or can
+     *  manage the DELEGATION_CERT_INSTALL scope.
+     *  If that check succeeds, ensure the profile owner was granted access to device
+     *  identifiers. The grant is transitive: The delegated cert installer is implicitly allowed
+     *  access to device identifiers in this case as part of the delegation.
+     */
+    @VisibleForTesting
+    public void enforceCallerCanRequestDeviceIdAttestation(
             ComponentName who, String callerPackage, int callerUid) throws SecurityException {
-        if (who == null) {
-            if (!mOwners.hasDeviceOwner()) {
-                throw new SecurityException("Not in Device Owner mode.");
+        final int userId = UserHandle.getUserId(callerUid);
+
+        /**
+         *  First check if there's a profile owner because the device could be in COMP mode (where
+         *  there's a device owner and profile owner on the same device).
+         *  If the caller is from the work profile, then it must be the PO or the delegate, and
+         *  it must have the right permission to access device identifiers.
+         */
+        if (hasProfileOwner(userId)) {
+            // Make sure that the caller is the profile owner or delegate.
+            enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+                    DELEGATION_CERT_INSTALL);
+            // Verify that the profile owner was granted access to Device IDs.
+            if (canProfileOwnerAccessDeviceIds(userId)) {
+                return;
             }
-            if (UserHandle.getUserId(callerUid) != mOwners.getDeviceOwnerUserId()) {
-                throw new SecurityException("Caller not from device owner user");
-            }
-            if (!isCallerDelegate(callerPackage, DELEGATION_CERT_INSTALL)) {
-                throw new SecurityException("Caller with uid " + mInjector.binderGetCallingUid() +
-                        "has no permission to generate keys.");
-            }
-        } else {
-            // Caller provided - check it is the device owner.
-            enforceDeviceOwner(who);
+            throw new SecurityException(
+                    "Profile Owner is not allowed to access Device IDs.");
         }
+
+        // If not, fall back to the device owner check.
+        enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER,
+                DELEGATION_CERT_INSTALL);
     }
 
     @VisibleForTesting
@@ -5499,7 +5535,7 @@
         final int callingUid = mInjector.binderGetCallingUid();
 
         if (deviceIdAttestationRequired && attestationUtilsFlags.length > 0) {
-            enforceIsDeviceOwnerOrCertInstallerOfDeviceOwner(who, callerPackage, callingUid);
+            enforceCallerCanRequestDeviceIdAttestation(who, callerPackage, callingUid);
         } else {
             enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
                     DELEGATION_CERT_INSTALL);
@@ -7365,6 +7401,18 @@
         return who != null && who.equals(profileOwner);
     }
 
+    private boolean hasProfileOwner(int userId) {
+        synchronized (getLockObject()) {
+            return mOwners.hasProfileOwner(userId);
+        }
+    }
+
+    private boolean canProfileOwnerAccessDeviceIds(int userId) {
+        synchronized (getLockObject()) {
+            return mOwners.canProfileOwnerAccessDeviceIds(userId);
+        }
+    }
+
     @Override
     public ComponentName getDeviceOwnerComponent(boolean callingUserOnly) {
         if (!mHasFeature) {
@@ -11583,6 +11631,53 @@
         return false;
     }
 
+    private boolean hasGrantProfileOwnerDevcieIdAccessPermission() {
+        return mContext.checkCallingPermission(
+                android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    @Override
+    public void grantDeviceIdsAccessToProfileOwner(ComponentName who, int userId) {
+        // As the caller is the system, it must specify the component name of the profile owner
+        // as a sanity / safety check.
+        Preconditions.checkNotNull(who);
+
+        if (!mHasFeature) {
+            return;
+        }
+
+        // Only privileged system apps can grant the Profile Owner access to Device IDs.
+        if (!(isCallerWithSystemUid() || isAdb()
+                || hasGrantProfileOwnerDevcieIdAccessPermission())) {
+            throw new SecurityException(
+                    "Only the system can grant Device IDs access for a profile owner.");
+        }
+
+        if (isAdb() && hasIncompatibleAccountsOrNonAdbNoLock(userId, who)) {
+            throw new SecurityException(
+                    "Can only be called from ADB if the device has no accounts.");
+        }
+
+        // Grant access under lock.
+        synchronized (getLockObject()) {
+            // Sanity check: Make sure that the user has a profile owner and that the specified
+            // component is the profile owner of that user.
+            if (!isProfileOwner(who, userId)) {
+                throw new IllegalArgumentException(String.format(
+                        "Component %s is not a Profile Owner of user %d",
+                        who.flattenToString(), userId));
+            }
+
+            Slog.i(LOG_TAG, String.format("Granting Device ID access to %s, for user %d",
+                        who.flattenToString(), userId));
+
+            // setProfileOwnerCanAccessDeviceIds will trigger writing of the profile owner
+            // data, no need to do it manually.
+            mOwners.setProfileOwnerCanAccessDeviceIds(userId);
+        }
+    }
+
     private void pushMeteredDisabledPackagesLocked(int userId) {
         mInjector.getNetworkPolicyManagerInternal().setMeteredRestrictedPackages(
                 getMeteredDisabledPackagesLocked(userId), userId);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 632f0aa..ee1c1df3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -42,6 +42,8 @@
 import com.android.internal.util.FastXmlSerializer;
 import com.android.server.LocalServices;
 
+import libcore.io.IoUtils;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -58,8 +60,6 @@
 import java.util.Objects;
 import java.util.Set;
 
-import libcore.io.IoUtils;
-
 /**
  * Stores and restores state for the Device and Profile owners and related device-wide information.
  * By definition there can be only one device owner, but there may be a profile owner for each user.
@@ -99,6 +99,7 @@
     private static final String ATTR_USER_RESTRICTIONS_MIGRATED = "userRestrictionsMigrated";
     private static final String ATTR_FREEZE_RECORD_START = "start";
     private static final String ATTR_FREEZE_RECORD_END = "end";
+    private static final String ATTR_CAN_ACCESS_DEVICE_IDS = "canAccessDeviceIds";
 
     private final UserManager mUserManager;
     private final UserManagerInternal mUserManagerInternal;
@@ -264,8 +265,12 @@
     void setDeviceOwnerWithRestrictionsMigrated(ComponentName admin, String ownerName, int userId,
             boolean userRestrictionsMigrated) {
         synchronized (mLock) {
+            // A device owner is allowed to access device identifiers. Even though this flag
+            // is not currently checked for device owner, it is set to true here so that it is
+            // semantically compatible with the meaning of this flag.
             mDeviceOwner = new OwnerInfo(ownerName, admin, userRestrictionsMigrated,
-                    /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/ null);
+                    /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/
+                    null, /* canAccessDeviceIds =*/true);
             mDeviceOwnerUserId = userId;
 
             mUserManagerInternal.setDeviceManaged(true);
@@ -290,7 +295,7 @@
             // For a newly set PO, there's no need for migration.
             mProfileOwners.put(userId, new OwnerInfo(ownerName, admin,
                     /* userRestrictionsMigrated =*/ true, /* remoteBugreportUri =*/ null,
-                    /* remoteBugreportHash =*/ null));
+                    /* remoteBugreportHash =*/ null, /* canAccessDeviceIds =*/ false));
             mUserManagerInternal.setUserManaged(userId, true);
             pushToPackageManagerLocked();
             pushToAppOpsLocked();
@@ -311,7 +316,8 @@
             final OwnerInfo ownerInfo = mProfileOwners.get(userId);
             final OwnerInfo newOwnerInfo = new OwnerInfo(target.getPackageName(), target,
                     ownerInfo.userRestrictionsMigrated, ownerInfo.remoteBugreportUri,
-                    ownerInfo.remoteBugreportHash);
+                    ownerInfo.remoteBugreportHash, /* canAccessDeviceIds =*/
+                    ownerInfo.canAccessDeviceIds);
             mProfileOwners.put(userId, newOwnerInfo);
             pushToPackageManagerLocked();
             pushToAppOpsLocked();
@@ -324,7 +330,8 @@
             // See DevicePolicyManagerService#getDeviceOwnerName
             mDeviceOwner = new OwnerInfo(null, target,
                     mDeviceOwner.userRestrictionsMigrated, mDeviceOwner.remoteBugreportUri,
-                    mDeviceOwner.remoteBugreportHash);
+                    mDeviceOwner.remoteBugreportHash, /* canAccessDeviceIds =*/
+                    mDeviceOwner.canAccessDeviceIds);
             pushToPackageManagerLocked();
             pushToAppOpsLocked();
         }
@@ -351,6 +358,17 @@
         }
     }
 
+    /**
+     * Returns true if {@code userId} has a profile owner and that profile owner was granted
+     * the ability to access device identifiers.
+     */
+    boolean canProfileOwnerAccessDeviceIds(int userId) {
+        synchronized (mLock) {
+            OwnerInfo profileOwner = mProfileOwners.get(userId);
+            return profileOwner != null ? profileOwner.canAccessDeviceIds : false;
+        }
+    }
+
     Set<Integer> getProfileOwnerKeys() {
         synchronized (mLock) {
             return mProfileOwners.keySet();
@@ -486,6 +504,20 @@
         }
     }
 
+    /** Sets the grant to access device IDs, and also writes to file. */
+    void setProfileOwnerCanAccessDeviceIds(int userId) {
+        synchronized (mLock) {
+            OwnerInfo profileOwner = mProfileOwners.get(userId);
+            if (profileOwner != null) {
+                profileOwner.canAccessDeviceIds = true;
+            } else {
+                Slog.e(TAG, String.format(
+                        "Cannot grant Device IDs access for user %d, no profile owner.", userId));
+            }
+            writeProfileOwner(userId);
+        }
+    }
+
     private boolean readLegacyOwnerFileLocked(File file) {
         if (!file.exists()) {
             // Already migrated or the device has no owners.
@@ -507,7 +539,7 @@
                     String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
                     mDeviceOwner = new OwnerInfo(name, packageName,
                             /* userRestrictionsMigrated =*/ false, /* remoteBugreportUri =*/ null,
-                            /* remoteBugreportHash =*/ null);
+                            /* remoteBugreportHash =*/ null, /* canAccessDeviceIds =*/ true);
                     mDeviceOwnerUserId = UserHandle.USER_SYSTEM;
                 } else if (tag.equals(TAG_DEVICE_INITIALIZER)) {
                     // Deprecated tag
@@ -523,7 +555,8 @@
                                 profileOwnerComponentStr);
                         if (admin != null) {
                             profileOwnerInfo = new OwnerInfo(profileOwnerName, admin,
-                                /* userRestrictionsMigrated =*/ false, null, null);
+                                    /* userRestrictionsMigrated =*/ false, null,
+                                    null, /* canAccessDeviceIds =*/ false);
                         } else {
                             // This shouldn't happen but switch from package name -> component name
                             // might have written bad device owner files. b/17652534
@@ -534,7 +567,8 @@
                     if (profileOwnerInfo == null) {
                         profileOwnerInfo = new OwnerInfo(profileOwnerName, profileOwnerPackageName,
                                 /* userRestrictionsMigrated =*/ false,
-                                /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/ null);
+                                /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/
+                                null, /* canAccessDeviceIds =*/ false);
                     }
                     mProfileOwners.put(userId, profileOwnerInfo);
                 } else if (TAG_SYSTEM_UPDATE_POLICY.equals(tag)) {
@@ -894,25 +928,28 @@
         public boolean userRestrictionsMigrated;
         public String remoteBugreportUri;
         public String remoteBugreportHash;
+        public boolean canAccessDeviceIds;
 
         public OwnerInfo(String name, String packageName, boolean userRestrictionsMigrated,
-                String remoteBugreportUri, String remoteBugreportHash) {
+                String remoteBugreportUri, String remoteBugreportHash, boolean canAccessDeviceIds) {
             this.name = name;
             this.packageName = packageName;
             this.admin = new ComponentName(packageName, "");
             this.userRestrictionsMigrated = userRestrictionsMigrated;
             this.remoteBugreportUri = remoteBugreportUri;
             this.remoteBugreportHash = remoteBugreportHash;
+            this.canAccessDeviceIds = canAccessDeviceIds;
         }
 
         public OwnerInfo(String name, ComponentName admin, boolean userRestrictionsMigrated,
-                String remoteBugreportUri, String remoteBugreportHash) {
+                String remoteBugreportUri, String remoteBugreportHash, boolean canAccessDeviceIds) {
             this.name = name;
             this.admin = admin;
             this.packageName = admin.getPackageName();
             this.userRestrictionsMigrated = userRestrictionsMigrated;
             this.remoteBugreportUri = remoteBugreportUri;
             this.remoteBugreportHash = remoteBugreportHash;
+            this.canAccessDeviceIds = canAccessDeviceIds;
         }
 
         public void writeToXml(XmlSerializer out, String tag) throws IOException {
@@ -932,6 +969,10 @@
             if (remoteBugreportHash != null) {
                 out.attribute(null, ATTR_REMOTE_BUGREPORT_HASH, remoteBugreportHash);
             }
+            if (canAccessDeviceIds) {
+                out.attribute(null, ATTR_CAN_ACCESS_DEVICE_IDS,
+                        String.valueOf(canAccessDeviceIds));
+            }
             out.endTag(null, tag);
         }
 
@@ -948,13 +989,17 @@
                     ATTR_REMOTE_BUGREPORT_URI);
             final String remoteBugreportHash = parser.getAttributeValue(null,
                     ATTR_REMOTE_BUGREPORT_HASH);
+            final String canAccessDeviceIdsStr =
+                    parser.getAttributeValue(null, ATTR_CAN_ACCESS_DEVICE_IDS);
+            final boolean canAccessDeviceIds =
+                    ("true".equals(canAccessDeviceIdsStr));
 
             // Has component name?  If so, return [name, component]
             if (componentName != null) {
                 final ComponentName admin = ComponentName.unflattenFromString(componentName);
                 if (admin != null) {
                     return new OwnerInfo(name, admin, userRestrictionsMigrated,
-                            remoteBugreportUri, remoteBugreportHash);
+                            remoteBugreportUri, remoteBugreportHash, canAccessDeviceIds);
                 } else {
                     // This shouldn't happen but switch from package name -> component name
                     // might have written bad device owner files. b/17652534
@@ -965,13 +1010,14 @@
 
             // Else, build with [name, package]
             return new OwnerInfo(name, packageName, userRestrictionsMigrated, remoteBugreportUri,
-                    remoteBugreportHash);
+                    remoteBugreportHash, canAccessDeviceIds);
         }
 
         public void dump(String prefix, PrintWriter pw) {
             pw.println(prefix + "admin=" + admin);
             pw.println(prefix + "name=" + name);
             pw.println(prefix + "package=" + packageName);
+            pw.println(prefix + "canAccessDeviceIds=" + canAccessDeviceIds);
         }
     }
 
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
index 25d1cc7..f7bb68c 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -42,6 +42,7 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
+import android.os.Binder;
 import android.os.HandlerThread;
 import android.os.PowerManager;
 import android.os.PowerSaveState;
@@ -139,7 +140,7 @@
      * specifically to prevent overloading the logs in production.
      */
     @Test
-    public void testMoreDebug_isFalse() throws Exception {
+    public void testMoreDebug_isFalse() {
         boolean moreDebug = BackupManagerService.MORE_DEBUG;
 
         assertThat(moreDebug).isFalse();
@@ -943,7 +944,7 @@
         backupManagerService.backupNow();
 
         assertThat(ShadowKeyValueBackupJob.getCallingUid()).isEqualTo(ShadowBinder.LOCAL_UID);
-        assertThat(ShadowBinder.getCallingUid()).isEqualTo(1);
+        assertThat(Binder.getCallingUid()).isEqualTo(1);
     }
 
     /**
@@ -961,7 +962,7 @@
         expectThrows(IllegalArgumentException.class, backupManagerService::backupNow);
         assertThat(ShadowKeyValueBackupJobException.getCallingUid())
                 .isEqualTo(ShadowBinder.LOCAL_UID);
-        assertThat(ShadowBinder.getCallingUid()).isEqualTo(1);
+        assertThat(Binder.getCallingUid()).isEqualTo(1);
     }
 
     private BackupManagerService createBackupManagerServiceForRequestBackup() {
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index 603a471..f307730 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -18,9 +18,7 @@
 
 import static com.android.server.backup.testing.TestUtils.runToEndOfTasks;
 
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -36,18 +34,16 @@
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.Process;
-import android.util.SparseArray;
+import android.util.Log;
 
 import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.Trampoline;
 import com.android.server.backup.TransportManager;
-import com.android.server.backup.internal.Operation;
 
 import org.mockito.stubbing.Answer;
 import org.robolectric.shadows.ShadowApplication;
 import org.robolectric.shadows.ShadowBinder;
-import org.robolectric.shadows.ShadowLog;
 
 import java.io.File;
 import java.lang.Thread.UncaughtExceptionHandler;
@@ -192,8 +188,7 @@
     public static HandlerThread startSilentBackupThread(String tag) {
         return startBackupThread(
                 (thread, e) ->
-                        ShadowLog.e(
-                                tag, "Uncaught exception in test thread " + thread.getName(), e));
+                        Log.e(tag, "Uncaught exception in test thread " + thread.getName(), e));
     }
 
     private BackupManagerServiceTestUtils() {}
diff --git a/services/robotests/src/com/android/server/backup/testing/TestUtils.java b/services/robotests/src/com/android/server/backup/testing/TestUtils.java
index 2f54513..3fe1f3f 100644
--- a/services/robotests/src/com/android/server/backup/testing/TestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/TestUtils.java
@@ -25,12 +25,12 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.MessageQueue;
+import android.os.SystemClock;
 
 import com.android.server.testing.shadows.ShadowEventLog;
 
 import org.robolectric.shadows.ShadowLog;
 import org.robolectric.shadows.ShadowLooper;
-import org.robolectric.shadows.ShadowSystemClock;
 
 import java.util.Arrays;
 import java.util.concurrent.Callable;
@@ -87,7 +87,7 @@
         // specific time to the looper the time of those messages will be before the looper's time.
         // To fix this we advance SystemClock as well since that is from where the handlers read
         // time.
-        ShadowSystemClock.setCurrentTimeMillis(shadowLooper.getScheduler().getCurrentTime());
+        SystemClock.setCurrentTimeMillis(shadowLooper.getScheduler().getCurrentTime());
     }
 
     /**
diff --git a/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java b/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java
index aac0a34..a8a258f 100644
--- a/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java
+++ b/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java
@@ -5,6 +5,7 @@
 import static org.mockito.Mockito.doReturn;
 
 import android.os.Looper;
+import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import android.util.NtpTrustedTime;
 
@@ -18,7 +19,6 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.shadows.ShadowLooper;
-import org.robolectric.shadows.ShadowSystemClock;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -74,7 +74,7 @@
         doReturn(true).when(mMockNtpTrustedTime).forceRefresh();
         doReturn(1L).when(mMockNtpTrustedTime).getCacheAge();
         doReturn(MOCK_NTP_TIME).when(mMockNtpTrustedTime).getCachedNtpTime();
-        ShadowSystemClock.sleep(NtpTimeHelper.RETRY_INTERVAL);
+        SystemClock.sleep(NtpTimeHelper.RETRY_INTERVAL);
 
         waitForTasksToBePostedOnHandlerAndRunThem();
         assertThat(mCountDownLatch.await(2, TimeUnit.SECONDS)).isTrue();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 16b127c..5dc6d83 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -4973,6 +4973,176 @@
         assertProfileOwnershipRevertedWithFakeTransferMetadata();
     }
 
+    public void testGrantDeviceIdsAccess_notToProfileOwner() throws Exception {
+        setupProfileOwner();
+        configureContextForAccess(mContext, false);
+
+        assertExpectException(SecurityException.class, /* messageRegex= */ null,
+                () -> dpm.setProfileOwnerCanAccessDeviceIdsForUser(admin2,
+                        UserHandle.of(DpmMockContext.CALLER_UID)));
+    }
+
+    public void testGrantDeviceIdsAccess_notByAuthorizedCaller() throws Exception {
+        setupProfileOwner();
+        configureContextForAccess(mContext, false);
+
+        assertExpectException(SecurityException.class, /* messageRegex= */ null,
+                () -> dpm.setProfileOwnerCanAccessDeviceIdsForUser(admin1,
+                        UserHandle.of(DpmMockContext.CALLER_UID)));
+    }
+
+    public void testGrantDeviceIdsAccess_byAuthorizedSystemCaller() throws Exception {
+        setupProfileOwner();
+
+        // This method will throw if the system context could not call
+        // setProfileOwnerCanAccessDeviceIds successfully.
+        configureProfileOwnerForDeviceIdAccess(admin1, DpmMockContext.CALLER_USER_HANDLE);
+    }
+
+    private static void configureContextForAccess(DpmMockContext context, boolean granted) {
+        when(context.spiedContext.checkCallingPermission(
+                android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS))
+                .thenReturn(granted ? PackageManager.PERMISSION_GRANTED
+                        : PackageManager.PERMISSION_DENIED);
+    }
+
+    public void testGrantDeviceIdsAccess_byAuthorizedManagedProvisioning() throws Exception {
+        setupProfileOwner();
+
+        final long ident = mServiceContext.binder.clearCallingIdentity();
+        configureContextForAccess(mServiceContext, true);
+
+        mServiceContext.binder.callingUid =
+                UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE,
+                        DpmMockContext.CALLER_MANAGED_PROVISIONING_UID);
+        try {
+            runAsCaller(mServiceContext, dpms, dpm -> {
+                dpm.setProfileOwnerCanAccessDeviceIdsForUser(admin1,
+                        UserHandle.of(DpmMockContext.CALLER_USER_HANDLE));
+            });
+        } finally {
+            mServiceContext.binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    public void testEnforceCallerCanRequestDeviceIdAttestation_deviceOwnerCaller()
+            throws Exception {
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        setupDeviceOwner();
+        configureContextForAccess(mContext, false);
+
+        // Device owner should be allowed to request Device ID attestation.
+        dpms.enforceCallerCanRequestDeviceIdAttestation(admin1, admin1.getPackageName(),
+                DpmMockContext.CALLER_SYSTEM_USER_UID);
+
+        // Another package must not be allowed to request Device ID attestation.
+        assertExpectException(SecurityException.class, null,
+                () -> dpms.enforceCallerCanRequestDeviceIdAttestation(null,
+                        admin2.getPackageName(), DpmMockContext.CALLER_UID));
+        // Another component that is not the admin must not be allowed to request Device ID
+        // attestation.
+        assertExpectException(SecurityException.class, null,
+                () -> dpms.enforceCallerCanRequestDeviceIdAttestation(admin2,
+                        admin1.getPackageName(), DpmMockContext.CALLER_UID));
+    }
+
+    public void testEnforceCallerCanRequestDeviceIdAttestation_profileOwnerCaller()
+            throws Exception {
+        configureContextForAccess(mContext, false);
+
+        // Make sure a security exception is thrown if the device has no profile owner.
+        assertExpectException(SecurityException.class, null,
+                () -> dpms.enforceCallerCanRequestDeviceIdAttestation(admin1,
+                        admin1.getPackageName(), DpmMockContext.CALLER_SYSTEM_USER_UID));
+
+        setupProfileOwner();
+        configureProfileOwnerForDeviceIdAccess(admin1, DpmMockContext.CALLER_USER_HANDLE);
+
+        // The profile owner is allowed to request Device ID attestation.
+        mServiceContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        dpms.enforceCallerCanRequestDeviceIdAttestation(admin1, admin1.getPackageName(),
+                DpmMockContext.CALLER_UID);
+        // But not another package.
+        assertExpectException(SecurityException.class, null,
+                () -> dpms.enforceCallerCanRequestDeviceIdAttestation(null,
+                        admin2.getPackageName(), DpmMockContext.CALLER_UID));
+        // Or another component which is not the admin.
+        assertExpectException(SecurityException.class, null,
+                () -> dpms.enforceCallerCanRequestDeviceIdAttestation(admin2,
+                        admin2.getPackageName(), DpmMockContext.CALLER_UID));
+    }
+
+    public void runAsDelegatedCertInstaller(DpmRunnable action) throws Exception {
+        final long ident = mServiceContext.binder.clearCallingIdentity();
+
+        mServiceContext.binder.callingUid = UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE,
+                DpmMockContext.DELEGATE_CERT_INSTALLER_UID);
+        try {
+            runAsCaller(mServiceContext, dpms, action);
+        } finally {
+            mServiceContext.binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    public void testEnforceCallerCanRequestDeviceIdAttestation_delegateCaller() throws Exception {
+        setupProfileOwner();
+        markDelegatedCertInstallerAsInstalled();
+
+        // Configure a delegated cert installer.
+        runAsCaller(mServiceContext, dpms,
+                dpm -> dpm.setDelegatedScopes(admin1, DpmMockContext.DELEGATE_PACKAGE_NAME,
+                        Arrays.asList(DELEGATION_CERT_INSTALL)));
+
+        configureProfileOwnerForDeviceIdAccess(admin1, DpmMockContext.CALLER_USER_HANDLE);
+
+        // Make sure that the profile owner can still request Device ID attestation.
+        mServiceContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        dpms.enforceCallerCanRequestDeviceIdAttestation(admin1, admin1.getPackageName(),
+                DpmMockContext.CALLER_UID);
+
+        runAsDelegatedCertInstaller(dpm -> {
+            dpms.enforceCallerCanRequestDeviceIdAttestation(null,
+                    DpmMockContext.DELEGATE_PACKAGE_NAME,
+                    UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE,
+                            DpmMockContext.DELEGATE_CERT_INSTALLER_UID));
+        });
+    }
+
+    public void testEnforceCallerCanRequestDeviceIdAttestation_delegateCallerWithoutPermissions()
+            throws Exception {
+        setupProfileOwner();
+        markDelegatedCertInstallerAsInstalled();
+
+        // Configure a delegated cert installer.
+        runAsCaller(mServiceContext, dpms,
+                dpm -> dpm.setDelegatedScopes(admin1, DpmMockContext.DELEGATE_PACKAGE_NAME,
+                        Arrays.asList(DELEGATION_CERT_INSTALL)));
+
+
+        assertExpectException(SecurityException.class, null,
+                () -> dpms.enforceCallerCanRequestDeviceIdAttestation(admin1,
+                        admin1.getPackageName(),
+                        DpmMockContext.CALLER_UID));
+
+        runAsDelegatedCertInstaller(dpm -> {
+            assertExpectException(SecurityException.class, /* messageRegex= */ null,
+                    () -> dpms.enforceCallerCanRequestDeviceIdAttestation(null,
+                            DpmMockContext.DELEGATE_PACKAGE_NAME,
+                            UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE,
+                                    DpmMockContext.DELEGATE_CERT_INSTALLER_UID)));
+        });
+    }
+
+    private void configureProfileOwnerForDeviceIdAccess(ComponentName who, int userId) {
+        final long ident = mServiceContext.binder.clearCallingIdentity();
+        mServiceContext.binder.callingUid =
+                UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE, DpmMockContext.SYSTEM_UID);
+        runAsCaller(mServiceContext, dpms, dpm -> {
+            dpm.setProfileOwnerCanAccessDeviceIdsForUser(who, UserHandle.of(userId));
+        });
+        mServiceContext.binder.restoreCallingIdentity(ident);
+    }
+
     // admin1 is the outgoing DPC, adminAnotherPakcage is the incoming one.
     private void assertDeviceOwnershipRevertedWithFakeTransferMetadata() throws Exception {
         writeFakeTransferMetadataFile(UserHandle.USER_SYSTEM,
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index be00bb6..e411fb5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -59,6 +59,12 @@
     public static final int CALLER_UID = UserHandle.getUid(CALLER_USER_HANDLE, 20123);
 
     /**
+     * UID corresponding to {@link #CALLER_USER_HANDLE}.
+     */
+    public static final int CALLER_MANAGED_PROVISIONING_UID = UserHandle.getUid(CALLER_USER_HANDLE,
+            20125);
+
+    /**
      * UID used when a caller is on the system user.
      */
     public static final int CALLER_SYSTEM_USER_UID = 20321;
@@ -81,6 +87,10 @@
     public static final String ANOTHER_PACKAGE_NAME = "com.another.package.name";
     public static final int ANOTHER_UID = UserHandle.getUid(UserHandle.USER_SYSTEM, 18434);
 
+    public static final String DELEGATE_PACKAGE_NAME = "com.delegate.package.name";
+    public static final int DELEGATE_CERT_INSTALLER_UID = UserHandle.getUid(UserHandle.USER_SYSTEM,
+            18437);
+
     private final MockSystemServices mMockSystemServices;
 
     public static class MockBinder {
@@ -427,4 +437,9 @@
     public int getUserId() {
         return UserHandle.getUserId(binder.getCallingUid());
     }
+
+    @Override
+    public int checkCallingPermission(String permission) {
+        return spiedContext.checkCallingPermission(permission);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 0c8a787..a34c2ff 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -50,6 +50,7 @@
     public ComponentName admin3;
     public ComponentName adminAnotherPackage;
     public ComponentName adminNoPerm;
+    public ComponentName delegateCertInstaller;
 
     @Override
     protected void setUp() throws Exception {
@@ -66,6 +67,8 @@
         adminAnotherPackage = new ComponentName(DpmMockContext.ANOTHER_PACKAGE_NAME,
                 "whatever.random.class");
         adminNoPerm = new ComponentName(mRealTestContext, DummyDeviceAdmins.AdminNoPerm.class);
+        delegateCertInstaller = new ComponentName(DpmMockContext.DELEGATE_PACKAGE_NAME,
+                "some.random.class");
         mockSystemPropertiesToReturnDefault();
     }
 
@@ -130,6 +133,20 @@
                 eq(userId));
     }
 
+    protected void markDelegatedCertInstallerAsInstalled() throws Exception {
+        final ApplicationInfo ai = new ApplicationInfo();
+        ai.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+        ai.flags = ApplicationInfo.FLAG_HAS_CODE;
+        // Mark the package as installed on the work profile.
+        ai.uid = UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE,
+                DpmMockContext.DELEGATE_CERT_INSTALLER_UID);
+        ai.packageName = delegateCertInstaller.getPackageName();
+        ai.name = delegateCertInstaller.getClassName();
+
+        markPackageAsInstalled(delegateCertInstaller.getPackageName(), ai,
+                DpmMockContext.CALLER_USER_HANDLE);
+    }
+
     protected void setUpPackageManagerForAdmin(ComponentName admin, int packageUid)
             throws Exception {
         setUpPackageManagerForAdmin(admin, packageUid,
diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
new file mode 100644
index 0000000..7cf7df13
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.IPowerManager;
+import android.os.IThermalEventListener;
+import android.os.IThermalStatusListener;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.Temperature;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.SystemService;
+import com.android.server.power.ThermalManagerService.ThermalHalWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server
+ * /power/ThermalManagerServiceTest.java
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ThermalManagerServiceTest {
+    private static final long CALLBACK_TIMEOUT_MILLI_SEC = 5000;
+    private ThermalManagerService mService;
+    private ThermalHalFake mFakeHal;
+    private PowerManager mPowerManager;
+    @Mock
+    private Context mContext;
+    @Mock
+    private IPowerManager mIPowerManagerMock;
+    @Mock
+    private IThermalEventListener mEventListener1;
+    @Mock
+    private IThermalEventListener mEventListener2;
+    @Mock
+    private IThermalStatusListener mStatusListener1;
+    @Mock
+    private IThermalStatusListener mStatusListener2;
+
+    /**
+     * Fake Hal class.
+     */
+    private class ThermalHalFake extends ThermalHalWrapper {
+        private static final int INIT_STATUS = Temperature.THROTTLING_NONE;
+        private ArrayList<Temperature> mTemperatureList = new ArrayList<>();
+        private Temperature mSkin1 = new Temperature(0, Temperature.TYPE_SKIN, "skin1",
+                INIT_STATUS);
+        private Temperature mSkin2 = new Temperature(0, Temperature.TYPE_SKIN, "skin2",
+                INIT_STATUS);
+        private Temperature mBattery = new Temperature(0, Temperature.TYPE_BATTERY, "batt",
+                INIT_STATUS);
+        private Temperature mUsbPort = new Temperature(0, Temperature.TYPE_USB_PORT, "usbport",
+                INIT_STATUS);
+
+        ThermalHalFake() {
+            mTemperatureList.add(mSkin1);
+            mTemperatureList.add(mSkin2);
+            mTemperatureList.add(mBattery);
+            mTemperatureList.add(mUsbPort);
+        }
+
+        @Override
+        protected List<Temperature> getCurrentTemperatures(boolean shouldFilter, int type) {
+            return mTemperatureList;
+        }
+
+        @Override
+        protected boolean connectToHal() {
+            return true;
+        }
+
+        @Override
+        protected void dump(PrintWriter pw, String prefix) {
+            return;
+        }
+    }
+
+    private void assertTemperatureEquals(List<Temperature> expected, List<Temperature> value) {
+        assertEquals(new HashSet<>(expected), new HashSet<>(value));
+    }
+
+    @Before
+    public void setUp() throws RemoteException {
+        MockitoAnnotations.initMocks(this);
+        mFakeHal = new ThermalHalFake();
+        mPowerManager = new PowerManager(mContext, mIPowerManagerMock, null);
+        when(mContext.getSystemServiceName(PowerManager.class)).thenReturn(Context.POWER_SERVICE);
+        when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
+        resetListenerMock();
+        mService = new ThermalManagerService(mContext, mFakeHal);
+        // Register callbacks before AMS ready and no callback sent
+        assertTrue(mService.mService.registerThermalEventListener(mEventListener1));
+        assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
+        assertTrue(mService.mService.registerThermalEventListenerWithType(mEventListener2,
+                Temperature.TYPE_SKIN));
+        assertTrue(mService.mService.registerThermalStatusListener(mStatusListener2));
+        verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).notifyThrottling(any(Temperature.class));
+        verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).onStatusChange(anyInt());
+        verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).notifyThrottling(any(Temperature.class));
+        verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).onStatusChange(anyInt());
+        mService.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
+        ArgumentCaptor<Temperature> captor = ArgumentCaptor.forClass(Temperature.class);
+        verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(4)).notifyThrottling(captor.capture());
+        assertTemperatureEquals(mFakeHal.mTemperatureList, captor.getAllValues());
+        verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+        captor = ArgumentCaptor.forClass(Temperature.class);
+        verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(2)).notifyThrottling(captor.capture());
+        assertTemperatureEquals(new ArrayList<>(Arrays.asList(mFakeHal.mSkin1, mFakeHal.mSkin2)),
+                captor.getAllValues());
+        verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+    }
+
+    private void resetListenerMock() {
+        reset(mEventListener1);
+        reset(mStatusListener1);
+        reset(mEventListener2);
+        reset(mStatusListener2);
+        doReturn(mock(IBinder.class)).when(mEventListener1).asBinder();
+        doReturn(mock(IBinder.class)).when(mStatusListener1).asBinder();
+        doReturn(mock(IBinder.class)).when(mEventListener2).asBinder();
+        doReturn(mock(IBinder.class)).when(mStatusListener2).asBinder();
+    }
+
+    @Test
+    public void testRegister() throws RemoteException {
+        // Unregister all
+        assertTrue(mService.mService.unregisterThermalEventListener(mEventListener1));
+        assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener1));
+        assertTrue(mService.mService.unregisterThermalEventListener(mEventListener2));
+        assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener2));
+        resetListenerMock();
+        // Register callbacks and verify they are called
+        assertTrue(mService.mService.registerThermalEventListener(mEventListener1));
+        assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
+        ArgumentCaptor<Temperature> captor = ArgumentCaptor.forClass(Temperature.class);
+        verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(4)).notifyThrottling(captor.capture());
+        assertTemperatureEquals(mFakeHal.mTemperatureList, captor.getAllValues());
+        verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+        // Register new callbacks and verify old ones are not called (remained same) while new
+        // ones are called
+        assertTrue(mService.mService.registerThermalEventListenerWithType(mEventListener2,
+                Temperature.TYPE_SKIN));
+        assertTrue(mService.mService.registerThermalStatusListener(mStatusListener2));
+        verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(4)).notifyThrottling(any(Temperature.class));
+        verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+        captor = ArgumentCaptor.forClass(Temperature.class);
+        verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(2)).notifyThrottling(captor.capture());
+        assertTemperatureEquals(new ArrayList<>(Arrays.asList(mFakeHal.mSkin1, mFakeHal.mSkin2)),
+                captor.getAllValues());
+        verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+    }
+
+    @Test
+    public void testNotify() throws RemoteException {
+        int status = Temperature.THROTTLING_SEVERE;
+        Temperature newBattery = new Temperature(50, Temperature.TYPE_BATTERY, "batt", status);
+        mFakeHal.mCallback.onValues(newBattery);
+        verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).notifyThrottling(newBattery);
+        verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onStatusChange(status);
+        verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).notifyThrottling(newBattery);
+        verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onStatusChange(status);
+        resetListenerMock();
+        // Should only notify event not status
+        Temperature newSkin = new Temperature(50, Temperature.TYPE_SKIN, "skin1", status);
+        mFakeHal.mCallback.onValues(newSkin);
+        verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).notifyThrottling(newSkin);
+        verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).onStatusChange(anyInt());
+        verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).notifyThrottling(newSkin);
+        verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).onStatusChange(anyInt());
+        resetListenerMock();
+        // Back to None, should only notify event not status
+        status = Temperature.THROTTLING_NONE;
+        newBattery = new Temperature(50, Temperature.TYPE_BATTERY, "batt", status);
+        mFakeHal.mCallback.onValues(newBattery);
+        verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).notifyThrottling(newBattery);
+        verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).onStatusChange(anyInt());
+        verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).notifyThrottling(newBattery);
+        verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(0)).onStatusChange(anyInt());
+        resetListenerMock();
+        // Should also notify status
+        newSkin = new Temperature(50, Temperature.TYPE_SKIN, "skin1", status);
+        mFakeHal.mCallback.onValues(newSkin);
+        verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).notifyThrottling(newSkin);
+        verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onStatusChange(status);
+        verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).notifyThrottling(newSkin);
+        verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).onStatusChange(status);
+    }
+
+    @Test
+    public void testGetCurrentTemperatures() throws RemoteException {
+        assertTemperatureEquals(mFakeHal.getCurrentTemperatures(false, 0),
+                mService.mService.getCurrentTemperatures());
+        assertTemperatureEquals(mFakeHal.getCurrentTemperatures(true, Temperature.TYPE_SKIN),
+                mService.mService.getCurrentTemperaturesWithType(Temperature.TYPE_SKIN));
+    }
+
+    @Test
+    public void testGetCurrentStatus() throws RemoteException {
+        int status = Temperature.THROTTLING_WARNING;
+        Temperature newSkin = new Temperature(100, Temperature.TYPE_SKIN, "skin1", status);
+        mFakeHal.mCallback.onValues(newSkin);
+        assertEquals(status, mService.mService.getCurrentStatus());
+    }
+
+    @Test
+    public void testThermalShutdown() throws RemoteException {
+        int status = Temperature.THROTTLING_SHUTDOWN;
+        Temperature newSkin = new Temperature(100, Temperature.TYPE_SKIN, "skin1", status);
+        mFakeHal.mCallback.onValues(newSkin);
+        verify(mIPowerManagerMock, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+                .times(1)).shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
+    }
+
+    @Test
+    public void testNoHal() throws RemoteException {
+        mService = new ThermalManagerService(mContext);
+        // Do no call onActivityManagerReady to skip connect HAL
+        assertTrue(mService.mService.registerThermalEventListener(mEventListener1));
+        assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
+        assertTrue(mService.mService.unregisterThermalEventListener(mEventListener1));
+        assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener1));
+        assertEquals(0, mService.mService.getCurrentTemperatures().size());
+        assertEquals(0,
+                mService.mService.getCurrentTemperaturesWithType(Temperature.TYPE_SKIN).size());
+        assertEquals(Temperature.THROTTLING_NONE, mService.mService.getCurrentStatus());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
index 25e73e3..4ea6b39 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -48,7 +48,7 @@
         synchronized (mWm.mGlobalLock) {
             // No wallpaper
             final DisplayContent dc = createNewDisplay();
-            Bitmap wallpaperBitmap = mWm.mRoot.mWallpaperController.screenshotWallpaperLocked();
+            Bitmap wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
             assertNull(wallpaperBitmap);
 
             // No wallpaper WSA Surface
@@ -56,25 +56,25 @@
                     true, dc, true /* ownerCanManageAppTokens */);
             WindowState wallpaperWindow = createWindow(null /* parent */, TYPE_WALLPAPER,
                     wallpaperWindowToken, "wallpaperWindow");
-            wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
+            wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
             assertNull(wallpaperBitmap);
 
             // Wallpaper with not visible WSA surface.
             wallpaperWindow.mWinAnimator.mSurfaceController = windowSurfaceController;
             wallpaperWindow.mWinAnimator.mLastAlpha = 1;
-            wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
+            wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
             assertNull(wallpaperBitmap);
 
             when(windowSurfaceController.getShown()).thenReturn(true);
 
             // Wallpaper with WSA alpha set to 0.
             wallpaperWindow.mWinAnimator.mLastAlpha = 0;
-            wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
+            wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
             assertNull(wallpaperBitmap);
 
             // Wallpaper window with WSA Surface
             wallpaperWindow.mWinAnimator.mLastAlpha = 1;
-            wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
+            wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
             assertNotNull(wallpaperBitmap);
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index 80bb936..d6fea09 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -27,7 +27,6 @@
 import static org.mockito.Mockito.anyFloat;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -58,8 +57,8 @@
     public static class TestDisplayContent extends DisplayContent {
 
         private TestDisplayContent(Display display, WindowManagerService service,
-                WallpaperController wallpaperController, DisplayWindowController controller) {
-            super(display, service, wallpaperController, controller);
+                DisplayWindowController controller) {
+            super(display, service, controller);
         }
 
         /** Create a mocked default {@link DisplayContent}. */
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 53858c7..1eb46fb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -90,7 +90,6 @@
     WindowState mChildAppWindowAbove;
     WindowState mChildAppWindowBelow;
     HashSet<WindowState> mCommonWindows;
-    WallpaperController mWallpaperController;
 
     @Rule
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
@@ -119,8 +118,6 @@
             mWm = mWmRule.getWindowManagerService();
             beforeCreateDisplay();
 
-            mWallpaperController = new WallpaperController(mWm);
-
             context.getDisplay().getDisplayInfo(mDisplayInfo);
             mDisplayContent = createNewDisplay();
             mWm.mDisplayEnabled = true;
@@ -363,8 +360,7 @@
         final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
                 displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
         synchronized (mWm.mGlobalLock) {
-            return new DisplayContent(display, mWm, mWallpaperController,
-                    mock(DisplayWindowController.class));
+            return new DisplayContent(display, mWm, mock(DisplayWindowController.class));
         }
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 72d7c90..630a8bf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -47,12 +47,10 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.OutputStream;
+import java.io.InputStreamReader;
 import java.io.Reader;
 import java.util.ArrayList;
 
@@ -79,15 +77,10 @@
         final TaskRecord expected = createTaskRecord(64);
         expected.mLastNonFullscreenBounds = new Rect(50, 50, 100, 100);
 
-        final File serializedFile = serializeToFile(expected);
-
-        try {
-            final TaskRecord actual = restoreFromFile(serializedFile);
-            assertEquals(expected.taskId, actual.taskId);
-            assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds);
-        } finally {
-            serializedFile.delete();
-        }
+        final byte[] serializedBytes = serializeToBytes(expected);
+        final TaskRecord actual = restoreFromBytes(serializedBytes);
+        assertEquals(expected.taskId, actual.taskId);
+        assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds);
     }
 
     @Test
@@ -131,10 +124,8 @@
         assertTrue(task.returnsToHomeStack());
     }
 
-    private File serializeToFile(TaskRecord r) throws IOException, XmlPullParserException {
-        final File tmpFile = File.createTempFile(r.taskId + "_task_", "xml");
-
-        try (OutputStream os = new FileOutputStream(tmpFile)) {
+    private byte[] serializeToBytes(TaskRecord r) throws IOException, XmlPullParserException {
+        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
             final XmlSerializer serializer = Xml.newSerializer();
             serializer.setOutput(os, "UTF-8");
             serializer.startDocument(null, true);
@@ -142,13 +133,14 @@
             r.saveToXml(serializer);
             serializer.endTag(null, TASK_TAG);
             serializer.endDocument();
-        }
 
-        return tmpFile;
+            os.flush();
+            return os.toByteArray();
+        }
     }
 
-    private TaskRecord restoreFromFile(File file) throws IOException, XmlPullParserException {
-        try (Reader reader = new BufferedReader(new FileReader(file))) {
+    private TaskRecord restoreFromBytes(byte[] in) throws IOException, XmlPullParserException {
+        try (Reader reader = new InputStreamReader(new ByteArrayInputStream(in))) {
             final XmlPullParser parser = Xml.newPullParser();
             parser.setInput(reader);
             assertEquals(XmlPullParser.START_TAG, parser.next());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index cf34fe7..e56edab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -42,8 +42,8 @@
     public static class TestDisplayContent extends DisplayContent {
 
         private TestDisplayContent(Display display, WindowManagerService service,
-                WallpaperController wallpaperController, DisplayWindowController controller) {
-            super(display, service, wallpaperController, controller);
+                DisplayWindowController controller) {
+            super(display, service, controller);
         }
 
         /**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index bcc0e6b..5ad7c30 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1023,9 +1023,9 @@
     public static final String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool";
 
     /**
-     * Default Enhanced 4G LTE mode enabled. When this is {@code true}, Enhanced 4G LTE mode by
-     * default is on, otherwise if {@code false}, Enhanced 4G LTE mode by default is off.
-     * @hide
+     * Sets the default state for the "Enhanced 4G LTE" or "Advanced Calling" mode toggle set by the
+     * user. When this is {@code true}, this mode by default is on, otherwise if {@code false},
+     * this mode by default is off.
      */
     public static final String KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL =
             "enhanced_4g_lte_on_by_default_bool";
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 8324f00..79ed93e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7696,7 +7696,7 @@
         try {
             return getITelephony().isAvailable(getSubId(),
                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
-                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE, getOpPackageName());
+                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
         } catch (RemoteException | NullPointerException ex) {
             return false;
         }
@@ -8557,6 +8557,26 @@
         return UNKNOWN_CARRIER_ID;
     }
 
+     /**
+      * Returns carrier id based on MCCMNC only. This is for fallback when exact carrier id
+      * {@link #getSimCarrierId()} configurations are not found
+      *
+      * @return matching carrier id from passing mccmnc.
+      * @hide
+      */
+     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+     public int getCarrierIdFromMccMnc(String mccmnc) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getCarrierIdFromMccMnc(getSlotIndex(), mccmnc);
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+        }
+        return UNKNOWN_CARRIER_ID;
+    }
+
     /**
      * Return the application ID for the uicc application type like {@link #APPTYPE_CSIM}.
      * All uicc applications are uniquely identified by application ID. See ETSI 102.221 and 101.220
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index e06c372..122626f 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -28,17 +28,22 @@
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.SubscriptionManager;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.ITelephony;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.Executor;
 
 /**
@@ -52,6 +57,7 @@
  * @see #createForSubscriptionId(Context, int)
  * @hide
  */
+@SystemApi
 public class ImsMmTelManager {
 
     private static final String TAG = "ImsMmTelManager";
@@ -70,16 +76,12 @@
     /**
      * Register for IMS over IWLAN if WiFi signal quality is high enough. Do not hand over to LTE
      * registration if signal quality degrades.
-     * @hide
      */
-    @SystemApi
     public static final int WIFI_MODE_WIFI_ONLY = 0;
 
     /**
      * Prefer registering for IMS over LTE if LTE signal quality is high enough.
-     * @hide
      */
-    @SystemApi
     public static final int WIFI_MODE_CELLULAR_PREFERRED = 1;
 
     /**
@@ -91,13 +93,26 @@
 
     /**
      * Callback class for receiving Registration callback events.
-     * @see #addImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback)
-     * @see #removeImsRegistrationCallback(RegistrationCallback)
+     * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback)
+     * @see #unregisterImsRegistrationCallback(RegistrationCallback)
      */
     public static class RegistrationCallback {
 
         private static class RegistrationBinder extends IImsRegistrationCallback.Stub {
 
+            // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN
+            // and WWAN are more accurate constants.
+            private static final Map<Integer, Integer> IMS_REG_TO_ACCESS_TYPE_MAP =
+                    new HashMap<Integer, Integer>() {{
+                        // Map NONE to -1 to make sure that we handle the REGISTRATION_TECH_NONE
+                        // case, since it is defined.
+                        put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, -1);
+                        put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+                                AccessNetworkConstants.TransportType.WWAN);
+                        put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+                                AccessNetworkConstants.TransportType.WLAN);
+                    }};
+
             private final RegistrationCallback mLocalCallback;
             private Executor mExecutor;
 
@@ -109,16 +124,16 @@
             public void onRegistered(int imsRadioTech) {
                 if (mLocalCallback == null) return;
 
-                Binder.withCleanCallingIdentity(() ->
-                        mExecutor.execute(() -> mLocalCallback.onRegistered(imsRadioTech)));
+                Binder.withCleanCallingIdentity(() -> mExecutor.execute(() ->
+                        mLocalCallback.onRegistered(getAccessType(imsRadioTech))));
             }
 
             @Override
             public void onRegistering(int imsRadioTech) {
                 if (mLocalCallback == null) return;
 
-                Binder.withCleanCallingIdentity(() ->
-                        mExecutor.execute(() -> mLocalCallback.onRegistering(imsRadioTech)));
+                Binder.withCleanCallingIdentity(() -> mExecutor.execute(() ->
+                        mLocalCallback.onRegistering(getAccessType(imsRadioTech))));
             }
 
             @Override
@@ -134,8 +149,8 @@
                 if (mLocalCallback == null) return;
 
                 Binder.withCleanCallingIdentity(() ->
-                        mExecutor.execute(() ->
-                                mLocalCallback.onTechnologyChangeFailed(imsRadioTech, info)));
+                        mExecutor.execute(() -> mLocalCallback.onTechnologyChangeFailed(
+                                getAccessType(imsRadioTech), info)));
             }
 
             @Override
@@ -150,6 +165,15 @@
             private void setExecutor(Executor executor) {
                 mExecutor = executor;
             }
+
+            private static int getAccessType(int regType) {
+                if (!IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regType)) {
+                    Log.w("ImsMmTelManager", "RegistrationBinder - invalid regType returned: "
+                            + regType);
+                    return -1;
+                }
+                return IMS_REG_TO_ACCESS_TYPE_MAP.get(regType);
+            }
         }
 
         private final RegistrationBinder mBinder = new RegistrationBinder(this);
@@ -157,19 +181,19 @@
         /**
          * Notifies the framework when the IMS Provider is registered to the IMS network.
          *
-         * @param imsRadioTech the radio access technology. Valid values are defined in
-         * {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+         * @param imsTransportType the radio access technology. Valid values are defined in
+         * {@link android.telephony.AccessNetworkConstants.TransportType}.
          */
-        public void onRegistered(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+        public void onRegistered(int imsTransportType) {
         }
 
         /**
          * Notifies the framework when the IMS Provider is trying to register the IMS network.
          *
-         * @param imsRadioTech the radio access technology. Valid values are defined in
-         * {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+         * @param imsTransportType the radio access technology. Valid values are defined in
+         * {@link android.telephony.AccessNetworkConstants.TransportType}.
          */
-        public void onRegistering(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+        public void onRegistering(int imsTransportType) {
         }
 
         /**
@@ -182,14 +206,14 @@
 
         /**
          * A failure has occurred when trying to handover registration to another technology type,
-         * defined in {@link ImsRegistrationImplBase.ImsRegistrationTech}
+         * defined in {@link android.telephony.AccessNetworkConstants.TransportType}
          *
-         * @param imsRadioTech The {@link ImsRegistrationImplBase.ImsRegistrationTech} type that has
-         *         failed
+         * @param imsTransportType The
+         *         {@link android.telephony.AccessNetworkConstants.TransportType}
+         *         transport type that has failed to handover registration to.
          * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
          */
-        public void onTechnologyChangeFailed(
-                @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech, ImsReasonInfo info) {
+        public void onTechnologyChangeFailed(int imsTransportType, ImsReasonInfo info) {
         }
 
         /**
@@ -219,8 +243,8 @@
     /**
      * Receives IMS capability status updates from the ImsService.
      *
-     * @see #addMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback)
-     * @see #removeMmTelCapabilityCallback(CapabilityCallback)
+     * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback)
+     * @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
      */
     public static class CapabilityCallback {
 
@@ -285,13 +309,12 @@
         }
     }
 
-    private Context mContext;
     private int mSubId;
 
     /**
      * Create an instance of ImsManager for the subscription id specified.
      *
-     * @param context
+     * @param context The context to create this ImsMmTelManager instance within.
      * @param subId The ID of the subscription that this ImsMmTelManager will use.
      * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
      * @throws IllegalArgumentException if the subscription is invalid or
@@ -303,11 +326,15 @@
             throw new IllegalArgumentException("Invalid subscription ID");
         }
 
-        return new ImsMmTelManager(context, subId);
+        return new ImsMmTelManager(subId);
     }
 
-    private ImsMmTelManager(Context context, int subId) {
-        mContext = context;
+    /**
+     * Only visible for testing, use {@link #createForSubscriptionId(Context, int)} instead.
+     * @hide
+     */
+    @VisibleForTesting
+    public ImsMmTelManager(int subId) {
         mSubId = subId;
     }
 
@@ -315,14 +342,18 @@
      * Registers a {@link RegistrationCallback} with the system, which will provide registration
      * updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}. Use
      * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
-     * events and call {@link #removeImsRegistrationCallback(RegistrationCallback)} to clean up
+     * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up
      * after a subscription is removed.
+     *
+     * When the callback is registered, it will initiate the callback c to be called with the
+     * current registration state.
+     *
      * @param executor The executor the callback events should be run on.
      * @param c The {@link RegistrationCallback} to be added.
-     * @see #removeImsRegistrationCallback(RegistrationCallback)
+     * @see #unregisterImsRegistrationCallback(RegistrationCallback)
      */
-    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
-    public void addImsRegistrationCallback(@CallbackExecutor Executor executor,
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void registerImsRegistrationCallback(@CallbackExecutor Executor executor,
             @NonNull RegistrationCallback c) {
         if (c == null) {
             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
@@ -332,8 +363,7 @@
         }
         c.setExecutor(executor);
         try {
-            getITelephony().addImsRegistrationCallback(mSubId, c.getBinder(),
-                    mContext.getOpPackageName());
+            getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -344,16 +374,15 @@
      * up to avoid memory leaks or when the subscription is removed.
      * @param c The {@link RegistrationCallback} to be removed.
      * @see SubscriptionManager.OnSubscriptionsChangedListener
-     * @see #addImsRegistrationCallback(Executor, RegistrationCallback)
+     * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
      */
-    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
-    public void removeImsRegistrationCallback(@NonNull RegistrationCallback c) {
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) {
         if (c == null) {
             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
         }
         try {
-            getITelephony().removeImsRegistrationCallback(mSubId, c.getBinder(),
-                    mContext.getOpPackageName());
+            getITelephony().unregisterImsRegistrationCallback(mSubId, c.getBinder());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -364,14 +393,18 @@
      * updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}.
      * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
      * subscription changed events and call
-     * {@link #removeImsRegistrationCallback(RegistrationCallback)} to clean up after a subscription
-     * is removed.
+     * {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up after a
+     * subscription is removed.
+     *
+     * When the callback is registered, it will initiate the callback c to be called with the
+     * current capabilities.
+     *
      * @param executor The executor the callback events should be run on.
      * @param c The MmTel {@link CapabilityCallback} to be registered.
-     * @see #removeMmTelCapabilityCallback(CapabilityCallback)
+     * @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
      */
-    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
-    public void addMmTelCapabilityCallback(@CallbackExecutor Executor executor,
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void registerMmTelCapabilityCallback(@CallbackExecutor Executor executor,
             @NonNull CapabilityCallback c) {
         if (c == null) {
             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
@@ -381,8 +414,7 @@
         }
         c.setExecutor(executor);
         try {
-            getITelephony().addMmTelCapabilityCallback(mSubId, c.getBinder(),
-                    mContext.getOpPackageName());
+            getITelephony().registerMmTelCapabilityCallback(mSubId, c.getBinder());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -392,29 +424,42 @@
      * Removes an existing MmTel {@link CapabilityCallback}. Be sure to call this when cleaning
      * up to avoid memory leaks.
      * @param c The MmTel {@link CapabilityCallback} to be removed.
-     * @see #addMmTelCapabilityCallback(Executor, CapabilityCallback)
+     * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback)
      */
-    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
-    public void removeMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void unregisterMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
         if (c == null) {
             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
         }
         try {
-            getITelephony().removeMmTelCapabilityCallback(mSubId, c.getBinder(),
-                    mContext.getOpPackageName());
+            getITelephony().unregisterMmTelCapabilityCallback(mSubId, c.getBinder());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
     }
 
     /**
-     * Query the user's setting for whether or not to use MmTel capabilities over IMS,
-     * such as voice and video, depending on carrier configuration for the current subscription.
+     * Query the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to
+     * enable MmTel IMS features, depending on the carrier configuration for the current
+     * subscription. If this setting is enabled, IMS voice and video telephony over IWLAN/LTE will
+     * be enabled as long as the carrier has provisioned these services for the specified
+     * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on
+     * carrier requirements.
+     *
+     * Modifying this value may also trigger an IMS registration or deregistration, depending on
+     * whether or not the new value is enabled or disabled.
+     *
+     * Note: If the carrier configuration for advanced calling is not editable or hidden, this
+     * method will do nothing and will instead always use the default value.
+     *
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
      * @see #setAdvancedCallingSetting(boolean)
-     * @return true if the user’s setting for advanced calling is enabled and false otherwise.
-     * @hide
+     * @return true if the user's setting for advanced calling is enabled, false otherwise.
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isAdvancedCallingSettingEnabled() {
         try {
@@ -426,13 +471,25 @@
 
     /**
      * Modify the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to
-     * enable MmTel IMS features, such as voice and video calling, depending on the carrier
-     * configuration for the current subscription. Modifying this value may also trigger an IMS
-     * registration or deregistration, depending on the new value.
-     * @see #isAdvancedCallingEnabled()
-     * @hide
+     * enable MmTel IMS features, depending on the carrier configuration for the current
+     * subscription. If this setting is enabled, IMS voice and video telephony over IWLAN/LTE will
+     * be enabled as long as the carrier has provisioned these services for the specified
+     * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on
+     * carrier requirements.
+     *
+     * Modifying this value may also trigger an IMS registration or deregistration, depending on
+     * whether or not the new value is enabled or disabled.
+     *
+     * Note: If the carrier configuration for advanced calling is not editable or hidden, this
+     * method will do nothing and will instead always use the default value.
+     *
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
+     * @see #isAdvancedCallingSettingEnabled()
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setAdvancedCallingSetting(boolean isEnabled) {
         try {
@@ -464,12 +521,11 @@
      * @return {@code true} if the MmTel IMS capability is capable for this subscription, false
      *         otherwise.
      */
-    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
         try {
-            return getITelephony().isCapable(mSubId, capability, imsRegTech,
-                    mContext.getOpPackageName());
+            return getITelephony().isCapable(mSubId, capability, imsRegTech);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -492,12 +548,11 @@
      * @return {@code true} if the MmTel IMS capability is available for this subscription, false
      *         otherwise.
      */
-    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
         try {
-            return getITelephony().isAvailable(mSubId, capability, imsRegTech,
-                    mContext.getOpPackageName());
+            return getITelephony().isAvailable(mSubId, capability, imsRegTech);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -508,11 +563,10 @@
      * @return true if the user’s “Video Calling” setting is currently enabled.
      * @see #setVtSetting(boolean)
      */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isVtSettingEnabled() {
         try {
-            return getITelephony().isVtSettingEnabled(mSubId, mContext.getOpPackageName());
+            return getITelephony().isVtSettingEnabled(mSubId);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -521,9 +575,7 @@
     /**
      * Change the user's setting for Video Telephony and enable the Video Telephony capability.
      * @see #isVtSettingEnabled()
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVtSetting(boolean isEnabled) {
         try {
@@ -537,9 +589,7 @@
     /**
      * @return true if the user's setting for Voice over WiFi is enabled and false if it is not.
      * @see #setVoWiFiSetting(boolean)
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isVoWiFiSettingEnabled() {
         try {
@@ -553,9 +603,7 @@
      * Sets the user's setting for whether or not Voice over WiFi is enabled.
      * @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise=
      * @see #isVoWiFiSettingEnabled()
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiSetting(boolean isEnabled) {
         try {
@@ -570,9 +618,7 @@
      * @return true if the user's setting for Voice over WiFi while roaming is enabled, false
      * if disabled.
      * @see #setVoWiFiRoamingSetting(boolean)
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isVoWiFiRoamingSettingEnabled() {
         try {
@@ -587,9 +633,7 @@
      * @param isEnabled true if the user's setting for Voice over WiFi while roaming is enabled,
      *     false otherwise.
      * @see #isVoWiFiRoamingSettingEnabled()
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiRoamingSetting(boolean isEnabled) {
         try {
@@ -611,9 +655,7 @@
      * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
      * - {@link #WIFI_MODE_WIFI_PREFERRED}
      * @see #setVoWiFiSetting(boolean)
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiNonPersistent(boolean isCapable, int mode) {
         try {
@@ -631,9 +673,7 @@
      * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
      * - {@link #WIFI_MODE_WIFI_PREFERRED}
      * @see #setVoWiFiSetting(boolean)
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public @WiFiCallingMode int getVoWiFiModeSetting() {
         try {
@@ -651,9 +691,7 @@
      * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
      * - {@link #WIFI_MODE_WIFI_PREFERRED}
      * @see #getVoWiFiModeSetting()
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiModeSetting(@WiFiCallingMode int mode) {
         try {
@@ -674,9 +712,7 @@
      *     - {@link #WIFI_MODE_CELLULAR_PREFERRED}
      *     - {@link #WIFI_MODE_WIFI_PREFERRED}
      * @see #setVoWiFiRoamingSetting(boolean)
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
         try {
@@ -696,9 +732,7 @@
      *     - {@link #WIFI_MODE_CELLULAR_PREFERRED}
      *     - {@link #WIFI_MODE_WIFI_PREFERRED}
      * @see #getVoWiFiRoamingModeSetting()
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) {
         try {
@@ -712,9 +746,7 @@
     /**
      * Change the user's setting for RTT capability of this device.
      * @param isEnabled if true RTT will be enabled during calls.
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setRttCapabilitySetting(boolean isEnabled) {
         try {
@@ -729,9 +761,7 @@
      * @return true if TTY over VoLTE is supported
      * @see android.telecom.TelecomManager#getCurrentTtyMode
      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     boolean isTtyOverVolteEnabled() {
         try {
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index 3f22f98..b55866b 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -425,6 +425,12 @@
      */
     public final void addCapabilityCallback(IImsCapabilityCallback c) {
         mCapabilityCallbacks.register(c);
+        try {
+            // Notify the Capability callback that was just registered of the current capabilities.
+            c.onCapabilitiesStatusChanged(queryCapabilityStatus().mCapabilities);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "addCapabilityCallback: error accessing callback: " + e.getMessage());
+        }
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index f0e8586..fc42de5 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1372,6 +1372,15 @@
     String getSubscriptionPreciseCarrierName(int subId);
 
     /**
+     * Returns carrier id based on MCCMNC only. This will return a MNO carrier id used for fallback
+     * check when exact carrier id {@link #getSimCarrierId()} configurations are not found
+     *
+     * @return carrier id from passing mccmnc.
+     * @hide
+     */
+    int getCarrierIdFromMccMnc(int slotIndex, String mccmnc);
+
+    /**
      * Action set from carrier signalling broadcast receivers to enable/disable metered apns
      * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
      * @param subId the subscription ID that this action applies to.
@@ -1587,35 +1596,31 @@
     /**
      * Adds an IMS registration status callback for the subscription id specified.
      */
-    void addImsRegistrationCallback(int subId, IImsRegistrationCallback c,
-            String callingPackage);
+    void registerImsRegistrationCallback(int subId, IImsRegistrationCallback c);
      /**
       * Removes an existing IMS registration status callback for the subscription specified.
       */
-    void removeImsRegistrationCallback(int subId, IImsRegistrationCallback c,
-            String callingPackage);
+    void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback c);
 
     /**
      * Adds an IMS MmTel capabilities callback for the subscription specified.
      */
-    void addMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
-            String callingPackage);
+    void registerMmTelCapabilityCallback(int subId, IImsCapabilityCallback c);
 
     /**
      * Removes an existing IMS MmTel capabilities callback for the subscription specified.
      */
-    void removeMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
-            String callingPackage);
+    void unregisterMmTelCapabilityCallback(int subId, IImsCapabilityCallback c);
 
     /**
      * return true if the IMS MmTel capability for the given registration tech is capable.
      */
-    boolean isCapable(int subId, int capability, int regTech, String callingPackage);
+    boolean isCapable(int subId, int capability, int regTech);
 
     /**
      * return true if the IMS MmTel capability for the given registration tech is available.
      */
-    boolean isAvailable(int subId, int capability, int regTech, String callingPackage);
+    boolean isAvailable(int subId, int capability, int regTech);
 
     /**
      * Returns true if the user's setting for 4G LTE is enabled, for the subscription specified.
@@ -1630,7 +1635,7 @@
     /**
      * return true if the user's setting for VT is enabled for the subscription.
      */
-    boolean isVtSettingEnabled(int subId, String callingPackage);
+    boolean isVtSettingEnabled(int subId);
 
     /**
      * Modify the user's setting for whether or not VT is available for the subscrption specified.
@@ -1704,7 +1709,7 @@
      * Identify if the number is emergency number, based on all the active subscriptions.
      */
     boolean isCurrentEmergencyNumber(String number);
-    
+
     /**
      * Return a list of certs in hex string from loaded carrier privileges access rules.
      */
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index f91d74a..7842a1c 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -135,6 +135,7 @@
     method public boolean stopService(android.content.Intent);
     method public void unbindService(android.content.ServiceConnection);
     method public void unregisterReceiver(android.content.BroadcastReceiver);
+    method public void updateServiceGroup(android.content.ServiceConnection, int, int);
   }
 
   public deprecated class MockCursor implements android.database.Cursor {
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 66be6d9..ae6cd29 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -591,6 +591,11 @@
     }
 
     @Override
+    public void updateServiceGroup(ServiceConnection conn, int group, int importance) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public void unbindService(ServiceConnection conn) {
         throw new UnsupportedOperationException();
     }