Merge "Allow-list service-permission to not track latest api"
diff --git a/Android.bp b/Android.bp
index 4b3c22d..d234411 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1268,7 +1268,6 @@
 
 // TODO(b/145644363): move this to under StubLibraries.bp or ApiDocs.bp
 metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " +
-    "--ignore-classes-on-classpath " +
     "--hide-package com.android.server " +
     "--hide-package android.audio.policy.configuration.V7_0 " +
     "--error UnhiddenSystemApi " +
diff --git a/ApiDocs.bp b/ApiDocs.bp
index ba93a48..ada80bb 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -91,7 +91,9 @@
     arg_files: [
         "core/res/AndroidManifest.xml",
     ],
-    args: metalava_framework_docs_args,
+    args: metalava_framework_docs_args +
+        // Needed for hidden libcore annotations for now.
+        " --ignore-classes-on-classpath ",
     write_sdk_values: true,
 }
 
@@ -101,7 +103,10 @@
     arg_files: [
         "core/res/AndroidManifest.xml",
     ],
-    args: metalava_framework_docs_args + " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
+    args: metalava_framework_docs_args +
+        // Needed for hidden libcore annotations for now.
+        " --ignore-classes-on-classpath " +
+        " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
     write_sdk_values: true,
 }
 
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 6cabc57..d4abeee 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -50,7 +50,9 @@
         ":art.module.public.api{.public.stubs.source}",
         "**/package.html",
     ],
-    sdk_version: "core_platform",
+    sdk_version: "none",
+    system_modules: "none",
+    java_version: "1.8",
     arg_files: ["core/res/AndroidManifest.xml"],
     // TODO(b/147699819): remove below aidl includes.
     aidl: {
@@ -77,6 +79,7 @@
         "android.hardware.usb.gadget-V1.0-java",
         "android.hardware.vibrator-V1.3-java",
         "framework-protos",
+        "stable.core.platform.api.stubs",
         // There are a few classes from modules used as type arguments that
         // need to be resolved by metalava. For now, we can use a previously
         // finalized stub library to resolve them. If a new class gets added,
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index 8323d0b..437180d 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -71,7 +71,7 @@
   }
 
   // next, try to parse as a package:type/name string
-  if (auto resid = am.GetResourceId(res, "", fallback_package)) {
+  if (auto resid = am.GetResourceId(res, "", fallback_package); resid.ok()) {
     return *resid;
   }
 
@@ -94,7 +94,7 @@
     case Res_value::TYPE_STRING: {
       const ResStringPool* pool = am->GetStringPoolForCookie(value.cookie);
       out->append("\"");
-      if (auto str = pool->string8ObjectAt(value.data)) {
+      if (auto str = pool->string8ObjectAt(value.data); str.ok()) {
         out->append(*str);
       }
     } break;
diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp
index 7c55b64..4030b83 100644
--- a/cmds/idmap2/libidmap2/XmlParser.cpp
+++ b/cmds/idmap2/libidmap2/XmlParser.cpp
@@ -98,7 +98,7 @@
 
   switch ((*value).dataType) {
     case Res_value::TYPE_STRING: {
-      if (auto str = parser_.getStrings().string8ObjectAt((*value).data)) {
+      if (auto str = parser_.getStrings().string8ObjectAt((*value).data); str.ok()) {
         return std::string(str->string());
       }
       break;
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 29f6055..5e88d97d 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -3716,9 +3716,9 @@
 android.media.IRingtonePlayer$Stub$Proxy
 android.media.IRingtonePlayer$Stub
 android.media.IRingtonePlayer
-android.media.IStrategyPreferredDeviceDispatcher$Stub$Proxy
-android.media.IStrategyPreferredDeviceDispatcher$Stub
-android.media.IStrategyPreferredDeviceDispatcher
+android.media.IStrategyPreferredDevicesDispatcher$Stub$Proxy
+android.media.IStrategyPreferredDevicesDispatcher$Stub
+android.media.IStrategyPreferredDevicesDispatcher
 android.media.IVolumeController$Stub$Proxy
 android.media.IVolumeController$Stub
 android.media.IVolumeController
diff --git a/core/api/current.txt b/core/api/current.txt
index 3f173bd..65d3e4a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -19373,14 +19373,17 @@
   public class AudioManager {
     method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener);
     method public int abandonAudioFocusRequest(@NonNull android.media.AudioFocusRequest);
+    method public void addOnCommunicationDeviceChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnCommunicationDeviceChangedListener);
     method public void adjustStreamVolume(int, int, int);
     method public void adjustSuggestedStreamVolume(int, int, int);
     method public void adjustVolume(int, int);
+    method public void clearDeviceForCommunication();
     method public void dispatchMediaKeyEvent(android.view.KeyEvent);
     method public int generateAudioSessionId();
     method @NonNull public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
     method @NonNull public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
     method public int getAllowedCapturePolicy();
+    method @Nullable public android.media.AudioDeviceInfo getDeviceForCommunication();
     method public android.media.AudioDeviceInfo[] getDevices(int);
     method public java.util.List<android.media.MicrophoneInfo> getMicrophones() throws java.io.IOException;
     method public int getMode();
@@ -19415,11 +19418,13 @@
     method @Deprecated public void registerMediaButtonEventReceiver(android.app.PendingIntent);
     method @Deprecated public void registerRemoteControlClient(android.media.RemoteControlClient);
     method @Deprecated public boolean registerRemoteController(android.media.RemoteController);
+    method public void removeOnCommunicationDeviceChangedListener(@NonNull android.media.AudioManager.OnCommunicationDeviceChangedListener);
     method @Deprecated public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int);
     method public int requestAudioFocus(@NonNull android.media.AudioFocusRequest);
     method public void setAllowedCapturePolicy(int);
     method @Deprecated public void setBluetoothA2dpOn(boolean);
     method public void setBluetoothScoOn(boolean);
+    method public boolean setDeviceForCommunication(@NonNull android.media.AudioDeviceInfo);
     method public void setMicrophoneMute(boolean);
     method public void setMode(int);
     method public void setParameters(String);
@@ -19554,6 +19559,10 @@
     method public void onAudioFocusChange(int);
   }
 
+  public static interface AudioManager.OnCommunicationDeviceChangedListener {
+    method public void onCommunicationDeviceChanged(@Nullable android.media.AudioDeviceInfo);
+  }
+
   public final class AudioMetadata {
     method @NonNull public static android.media.AudioMetadataMap createMap();
   }
@@ -33225,8 +33234,8 @@
   }
 
   protected static interface ContactsContract.DataColumns {
-    field public static final String CARRIER_PRESENCE = "carrier_presence";
-    field public static final int CARRIER_PRESENCE_VT_CAPABLE = 1; // 0x1
+    field @Deprecated public static final String CARRIER_PRESENCE = "carrier_presence";
+    field @Deprecated public static final int CARRIER_PRESENCE_VT_CAPABLE = 1; // 0x1
     field public static final String DATA1 = "data1";
     field public static final String DATA10 = "data10";
     field public static final String DATA11 = "data11";
@@ -40257,7 +40266,7 @@
     field public static final String KEY_USE_ACS_FOR_RCS_BOOL = "use_acs_for_rcs_bool";
     field public static final String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool";
     field public static final String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
-    field public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
+    field @Deprecated public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
     field public static final String KEY_USE_RCS_SIP_OPTIONS_BOOL = "use_rcs_sip_options_bool";
     field public static final String KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL = "use_wfc_home_network_mode_in_roaming_network_bool";
     field public static final String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
@@ -40298,9 +40307,12 @@
   }
 
   public static final class CarrierConfigManager.Ims {
+    field public static final String KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL = "ims.enable_presence_capability_exchange_bool";
+    field public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL = "ims.enable_presence_group_subscribe_bool";
     field public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL = "ims.enable_presence_publish_bool";
     field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool";
     field public static final String KEY_PREFIX = "ims.";
+    field public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL = "ims.rcs_bulk_capability_exchange_bool";
     field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int";
   }
 
@@ -41762,7 +41774,7 @@
     method @Deprecated public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String);
     method public boolean isConcurrentVoiceAndDataSupported();
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isDataConnectionAllowed();
-    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabled();
+    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean isDataEnabled();
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabledForReason(int);
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled();
     method public boolean isEmergencyNumber(@NonNull String);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index cbd65d1..b4f1395 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -4145,8 +4145,11 @@
 
   public class AudioManager {
     method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException;
+    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException;
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForCapturePresetChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener) throws java.lang.SecurityException;
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener) throws java.lang.SecurityException;
     method public void clearAudioServerStateCallback();
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean clearPreferredDevicesForCapturePreset(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
     method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies();
@@ -4156,13 +4159,17 @@
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAttributes getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages();
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method public boolean isAudioServerRunning();
     method public boolean isHdmiSystemAudioSupported();
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
     method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForCapturePresetChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removePreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException;
@@ -4170,7 +4177,9 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) long);
     method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForCapturePreset(int, @NonNull android.media.AudioDeviceAttributes);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setVolumeIndexForAttributes(@NonNull android.media.AudioAttributes, int, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
@@ -4189,8 +4198,16 @@
     method public void onAudioServerUp();
   }
 
-  public static interface AudioManager.OnPreferredDeviceForStrategyChangedListener {
-    method public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes);
+  @Deprecated public static interface AudioManager.OnPreferredDeviceForStrategyChangedListener {
+    method @Deprecated public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes);
+  }
+
+  public static interface AudioManager.OnPreferredDevicesForCapturePresetChangedListener {
+    method public void onPreferredDevicesForCapturePresetChanged(int, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
+  }
+
+  public static interface AudioManager.OnPreferredDevicesForStrategyChangedListener {
+    method public void onPreferredDevicesForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
   }
 
   public abstract static class AudioManager.VolumeGroupCallback {
@@ -6968,12 +6985,14 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.os.connectivity.WifiBatteryStats getWifiBatteryStats();
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockAcquiredFromSource(@NonNull android.os.WorkSource);
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockReleasedFromSource(@NonNull android.os.WorkSource);
+    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportMobileRadioPowerState(boolean, int) throws java.lang.RuntimeException;
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiBatchedScanStartedFromSource(@NonNull android.os.WorkSource, @IntRange(from=0) int);
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiBatchedScanStoppedFromSource(@NonNull android.os.WorkSource);
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiMulticastDisabled(@NonNull android.os.WorkSource);
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiMulticastEnabled(@NonNull android.os.WorkSource);
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiOff();
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiOn();
+    method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiRadioPowerState(boolean, int) throws java.lang.RuntimeException;
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiRssiChanged(@IntRange(from=0xffffff81, to=0) int);
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiScanStartedFromSource(@NonNull android.os.WorkSource);
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiScanStoppedFromSource(@NonNull android.os.WorkSource);
@@ -7354,7 +7373,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void prepareForUnattendedUpdate(@NonNull android.content.Context, @NonNull String, @Nullable android.content.IntentSender) throws java.io.IOException;
     method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener, android.os.Handler) throws java.io.IOException;
     method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener) throws java.io.IOException;
-    method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException;
+    method @Deprecated @RequiresPermission(android.Manifest.permission.RECOVERY) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException;
     method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, boolean) throws java.io.IOException;
     method @RequiresPermission(allOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootWipeAb(android.content.Context, java.io.File, String) throws java.io.IOException;
     method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException;
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index feadd06..9cf9ce4 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -850,6 +850,7 @@
   }
 
   public class AudioManager {
+    method @Nullable public static android.media.AudioDeviceInfo getDeviceInfoFromType(int);
     method public boolean hasRegisteredDynamicPolicy();
   }
 
@@ -871,6 +872,9 @@
     method public static float getMasterBalance();
     method public static final int getNumStreamTypes();
     method public static int setMasterBalance(float);
+    field public static final int DEVICE_ROLE_DISABLED = 2; // 0x2
+    field public static final int DEVICE_ROLE_NONE = 0; // 0x0
+    field public static final int DEVICE_ROLE_PREFERRED = 1; // 0x1
     field public static final int STREAM_DEFAULT = -1; // 0xffffffff
   }
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 6edc8ea..9acf6756 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -53,7 +53,6 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
 import android.graphics.Bitmap;
-import android.net.NetworkUtils;
 import android.net.PrivateDnsConnectivityChecker;
 import android.net.ProxyInfo;
 import android.net.Uri;
@@ -90,6 +89,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.net.NetworkUtilsInternal;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.Preconditions;
 import com.android.org.conscrypt.TrustedCertificateStore;
@@ -11466,7 +11466,7 @@
             return PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
         }
 
-        if (NetworkUtils.isWeaklyValidatedHostname(privateDnsHost)) {
+        if (NetworkUtilsInternal.isWeaklyValidatedHostname(privateDnsHost)) {
             if (!PrivateDnsConnectivityChecker.canConnectToPrivateDnsServer(privateDnsHost)) {
                 return PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING;
             }
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 2dfbb3a..e4b2d70 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2933,6 +2933,16 @@
                             }
                         });
                     }
+                    synchronized (mBluetoothConnectionCallbackExecutorMap) {
+                        if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
+                            try {
+                                mService.registerBluetoothConnectionCallback(mConnectionCallback);
+                            } catch (RemoteException e) {
+                                Log.e(TAG, "onBluetoothServiceUp: Failed to register bluetooth"
+                                        + "connection callback", e);
+                            }
+                        }
+                    }
                 }
 
                 public void onBluetoothServiceDown() {
@@ -3582,25 +3592,25 @@
             return false;
         }
 
-        // If the callback map is empty, we register the service-to-app callback
-        if (mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
-            try {
-                mServiceLock.readLock().lock();
-                if (mService != null) {
-                    if (!mService.registerBluetoothConnectionCallback(mConnectionCallback)) {
-                        return false;
-                    }
-                }
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-                mBluetoothConnectionCallbackExecutorMap.remove(callback);
-            } finally {
-                mServiceLock.readLock().unlock();
-            }
-        }
-
-        // Adds the passed in callback to our map of callbacks to executors
         synchronized (mBluetoothConnectionCallbackExecutorMap) {
+            // If the callback map is empty, we register the service-to-app callback
+            if (mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
+                try {
+                    mServiceLock.readLock().lock();
+                    if (mService != null) {
+                        if (!mService.registerBluetoothConnectionCallback(mConnectionCallback)) {
+                            return false;
+                        }
+                    }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "", e);
+                    mBluetoothConnectionCallbackExecutorMap.remove(callback);
+                } finally {
+                    mServiceLock.readLock().unlock();
+                }
+            }
+
+            // Adds the passed in callback to our map of callbacks to executors
             if (mBluetoothConnectionCallbackExecutorMap.containsKey(callback)) {
                 throw new IllegalArgumentException("This callback has already been registered");
             }
diff --git a/core/java/android/net/INetworkManagementEventObserver.aidl b/core/java/android/net/INetworkManagementEventObserver.aidl
index f0fe92e..55ae218 100644
--- a/core/java/android/net/INetworkManagementEventObserver.aidl
+++ b/core/java/android/net/INetworkManagementEventObserver.aidl
@@ -85,11 +85,14 @@
     /**
      * Interface data activity status is changed.
      *
-     * @param iface The interface.
+     * @param label Unique identifier indicates the network type of the data activity change.
      * @param active  True if the interface is actively transmitting data, false if it is idle.
      * @param tsNanos Elapsed realtime in nanos when the state of the network interface changed.
+     * @param uid Uid of this event. It represents the uid that was responsible for waking the
+     *            radio. For those events that are reported by system itself, not from specific uid,
+     *            use -1 for the events which means no uid.
      */
-    void interfaceClassDataActivityChanged(String label, boolean active, long tsNanos);
+    void interfaceClassDataActivityChanged(String label, boolean active, long tsNanos, int uid);
 
     /**
      * Information about available DNS servers has been received.
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index fba7561..bf25602 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -26,9 +26,9 @@
 import static android.net.NetworkStatsHistory.Entry.UNKNOWN;
 import static android.net.NetworkStatsHistory.ParcelUtils.readLongArray;
 import static android.net.NetworkStatsHistory.ParcelUtils.writeLongArray;
-import static android.net.NetworkUtils.multiplySafeByRational;
 import static android.text.format.DateUtils.SECOND_IN_MILLIS;
 
+import static com.android.internal.net.NetworkUtilsInternal.multiplySafeByRational;
 import static com.android.internal.util.ArrayUtils.total;
 
 import android.compat.annotation.UnsupportedAppUsage;
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index d84ee2a..b5962c5 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -16,14 +16,9 @@
 
 package android.net;
 
-import static android.system.OsConstants.AF_INET;
-import static android.system.OsConstants.AF_INET6;
-
-import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.system.ErrnoException;
-import android.system.Os;
 import android.util.Log;
 import android.util.Pair;
 
@@ -155,14 +150,6 @@
     public static native Network getDnsNetwork() throws ErrnoException;
 
     /**
-     * Allow/Disallow creating AF_INET/AF_INET6 sockets and DNS lookups for current process.
-     *
-     * @param allowNetworking whether to allow or disallow creating AF_INET/AF_INET6 sockets
-     *                        and DNS lookups.
-     */
-    public static native void setAllowNetworkingForProcess(boolean allowNetworking);
-
-    /**
      * Get the tcp repair window associated with the {@code fd}.
      *
      * @param fd the tcp socket's {@link FileDescriptor}.
@@ -437,60 +424,4 @@
         return routedIPCount;
     }
 
-    private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET, AF_INET6};
-
-    /**
-     * Returns true if the hostname is weakly validated.
-     * @param hostname Name of host to validate.
-     * @return True if it's a valid-ish hostname.
-     *
-     * @hide
-     */
-    public static boolean isWeaklyValidatedHostname(@NonNull String hostname) {
-        // TODO(b/34953048): Use a validation method that permits more accurate,
-        // but still inexpensive, checking of likely valid DNS hostnames.
-        final String weakHostnameRegex = "^[a-zA-Z0-9_.-]+$";
-        if (!hostname.matches(weakHostnameRegex)) {
-            return false;
-        }
-
-        for (int address_family : ADDRESS_FAMILIES) {
-            if (Os.inet_pton(address_family, hostname) != null) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Safely multiple a value by a rational.
-     * <p>
-     * Internally it uses integer-based math whenever possible, but switches
-     * over to double-based math if values would overflow.
-     * @hide
-     */
-    public static long multiplySafeByRational(long value, long num, long den) {
-        if (den == 0) {
-            throw new ArithmeticException("Invalid Denominator");
-        }
-        long x = value;
-        long y = num;
-
-        // Logic shamelessly borrowed from Math.multiplyExact()
-        long r = x * y;
-        long ax = Math.abs(x);
-        long ay = Math.abs(y);
-        if (((ax | ay) >>> 31 != 0)) {
-            // Some bits greater than 2^31 that might cause overflow
-            // Check the result using the divide operator
-            // and check for the special case of Long.MIN_VALUE * -1
-            if (((y != 0) && (r / y != x)) ||
-                    (x == Long.MIN_VALUE && y == -1)) {
-                // Use double math to avoid overflowing
-                return (long) (((double) num / den) * value);
-            }
-        }
-        return r / den;
-    }
 }
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 46d1c1c..19c183f 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -30,6 +30,30 @@
 /**
  * VcnManager publishes APIs for applications to configure and manage Virtual Carrier Networks.
  *
+ * <p>A VCN creates a virtualization layer to allow MVNOs to aggregate heterogeneous physical
+ * networks, unifying them as a single carrier network. This enables infrastructure flexibility on
+ * the part of MVNOs without impacting user connectivity, abstracting the physical network
+ * technologies as an implementation detail of their public network.
+ *
+ * <p>Each VCN virtualizes an Carrier's network by building tunnels to a carrier's core network over
+ * carrier-managed physical links and supports a IP mobility layer to ensure seamless transitions
+ * between the underlying networks. Each VCN is configured based on a Subscription Group (see {@link
+ * android.telephony.SubscriptionManager}) and aggregates all networks that are brought up based on
+ * a profile or suggestion in the specified Subscription Group.
+ *
+ * <p>The VCN can be configured to expose one or more {@link android.net.Network}(s), each with
+ * different capabilities, allowing for APN virtualization.
+ *
+ * <p>If a tunnel fails to connect, or otherwise encounters a fatal error, the VCN will attempt to
+ * reestablish the connection. If the tunnel still has not reconnected after a system-determined
+ * timeout, the VCN Safe Mode (see below) will be entered.
+ *
+ * <p>The VCN Safe Mode ensures that users (and carriers) have a fallback to restore system
+ * connectivity to update profiles, diagnose issues, contact support, or perform other remediation
+ * tasks. In Safe Mode, the system will allow underlying cellular networks to be used as default.
+ * Additionally, during Safe Mode, the VCN will continue to retry the connections, and will
+ * automatically exit Safe Mode if all active tunnels connect successfully.
+ *
  * @hide
  */
 @SystemService(Context.VCN_MANAGEMENT_SERVICE)
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index a9585c6..258e58d 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.os.connectivity.CellularBatteryStats;
 import android.os.connectivity.WifiBatteryStats;
+import android.telephony.DataConnectionRealTimeInfo;
 
 import com.android.internal.app.IBatteryStats;
 
@@ -376,4 +377,50 @@
             e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Indicates that the radio power state has changed.
+     *
+     * @param isActive indicates if the mobile radio is powered.
+     * @param uid Uid of this event. For the active state it represents the uid that was responsible
+     *            for waking the radio, or -1 if the system was responsible for waking the radio.
+     *            For inactive state, the UID should always be -1.
+     * @throws RuntimeException if there are binder remote-invocation errors.
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportMobileRadioPowerState(boolean isActive, int uid) throws RuntimeException {
+        try {
+            mBatteryStats.noteMobileRadioPowerState(getDataConnectionPowerState(isActive),
+                    SystemClock.elapsedRealtimeNanos(), uid);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates that the wifi power state has changed.
+     *
+     * @param isActive indicates if the wifi radio is powered.
+     * @param uid Uid of this event. For the active state it represents the uid that was responsible
+     *            for waking the radio, or -1 if the system was responsible for waking the radio.
+     *            For inactive state, the UID should always be -1.
+     * @throws RuntimeException if there are binder remote-invocation errors.
+     */
+    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    public void reportWifiRadioPowerState(boolean isActive, int uid) throws RuntimeException {
+        try {
+            mBatteryStats.noteWifiRadioPowerState(getDataConnectionPowerState(isActive),
+                    SystemClock.elapsedRealtimeNanos(), uid);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    private static int getDataConnectionPowerState(boolean isActive) {
+        // TODO: DataConnectionRealTimeInfo is under telephony package but the constants are used
+        // for both Wifi and mobile. It would make more sense to separate the constants to a generic
+        // class or move it to generic package.
+        return isActive ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
+                : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+    }
 }
diff --git a/core/java/android/os/IRecoverySystem.aidl b/core/java/android/os/IRecoverySystem.aidl
index 5f8b932..2052883 100644
--- a/core/java/android/os/IRecoverySystem.aidl
+++ b/core/java/android/os/IRecoverySystem.aidl
@@ -30,5 +30,6 @@
     boolean requestLskf(in String packageName, in IntentSender sender);
     boolean clearLskf(in String packageName);
     boolean isLskfCaptured(in String packageName);
+    boolean rebootWithLskfAssumeSlotSwitch(in String packageName, in String reason);
     boolean rebootWithLskf(in String packageName, in String reason, in boolean slotSwitch);
 }
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 189e62b..6713de8 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -687,8 +687,8 @@
     }
 
     /**
-     * Request that the device reboot and apply the update that has been prepared. Callers are
-     * recommended to use {@link #rebootAndApply(Context, String, boolean)} instead.
+     * Request that the device reboot and apply the update that has been prepared. This API is
+     * deprecated, and is expected to be used by OTA only on devices running Android 11.
      *
      * @param context the Context to use.
      * @param updateToken this parameter is deprecated and won't be used. See details in
@@ -699,18 +699,18 @@
      *               unattended reboot or if the {@code updateToken} did not match the previously
      *               given token
      * @hide
+     * @deprecated Use {@link #rebootAndApply(Context, String, boolean)} instead
      */
     @SystemApi
-    @RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY,
-            android.Manifest.permission.REBOOT})
+    @RequiresPermission(android.Manifest.permission.RECOVERY)
     public static void rebootAndApply(@NonNull Context context, @NonNull String updateToken,
             @NonNull String reason) throws IOException {
         if (updateToken == null) {
             throw new NullPointerException("updateToken == null");
         }
         RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
-        // OTA is the sole user before S, and a slot switch is required for ota update.
-        if (!rs.rebootWithLskf(context.getPackageName(), reason, true)) {
+        // OTA is the sole user, who expects a slot switch.
+        if (!rs.rebootWithLskfAssumeSlotSwitch(context.getPackageName(), reason)) {
             throw new IOException("system not prepared to apply update");
         }
     }
@@ -738,7 +738,7 @@
      *
      * @param context the Context to use.
      * @param reason the reboot reason to give to the {@link PowerManager}
-     * @param slotSwitch true if the caller intends to switch the slot on an A/B device.
+     * @param slotSwitch true if the caller expects the slot to be switched on A/B devices.
      * @throws IOException if the reboot couldn't proceed because the device wasn't ready for an
      *               unattended reboot.
      * @hide
@@ -1387,6 +1387,21 @@
         }
     }
 
+
+    /**
+     * Calls the recovery system service to reboot and apply update. This is the legacy API and
+     * expects a slot switch for A/B devices.
+     *
+     */
+    private boolean rebootWithLskfAssumeSlotSwitch(String packageName, String reason)
+            throws IOException {
+        try {
+            return mService.rebootWithLskfAssumeSlotSwitch(packageName, reason);
+        } catch (RemoteException e) {
+            throw new IOException("could not reboot for update");
+        }
+    }
+
     /**
      * Internally, recovery treats each line of the command file as a separate
      * argv, so we only need to protect against newlines and nulls.
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index fa1b7d5..38a59f0 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -4305,13 +4305,23 @@
          * <P>
          * Type: INTEGER (A bitmask of CARRIER_PRESENCE_* fields)
          * </P>
+         *
+         * @deprecated The contacts database will only show presence
+         * information on devices where
+         * {@link android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL} is true,
+         * otherwise use {@link android.telephony.ims.RcsUceAdapter}.
          */
+        @Deprecated
         public static final String CARRIER_PRESENCE = "carrier_presence";
 
         /**
          * Indicates that the entry is Video Telephony (VT) capable on the
          * current carrier. An allowed bitmask of {@link #CARRIER_PRESENCE}.
+         *
+         * @deprecated Same as {@link DataColumns#CARRIER_PRESENCE}.
+         *
          */
+        @Deprecated
         public static final int CARRIER_PRESENCE_VT_CAPABLE = 0x01;
 
         /**
diff --git a/core/java/com/android/internal/net/NetworkUtilsInternal.java b/core/java/com/android/internal/net/NetworkUtilsInternal.java
new file mode 100644
index 0000000..571d7e7
--- /dev/null
+++ b/core/java/com/android/internal/net/NetworkUtilsInternal.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.net;
+
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
+import android.annotation.NonNull;
+import android.system.Os;
+
+/** @hide */
+public class NetworkUtilsInternal {
+
+    private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET, AF_INET6};
+
+    /**
+     * Allow/Disallow creating AF_INET/AF_INET6 sockets and DNS lookups for current process.
+     *
+     * @param allowNetworking whether to allow or disallow creating AF_INET/AF_INET6 sockets
+     *                        and DNS lookups.
+     */
+    public static native void setAllowNetworkingForProcess(boolean allowNetworking);
+
+    /**
+     * Returns true if the hostname is weakly validated.
+     * @param hostname Name of host to validate.
+     * @return True if it's a valid-ish hostname.
+     *
+     * @hide
+     */
+    public static boolean isWeaklyValidatedHostname(@NonNull String hostname) {
+        // TODO(b/34953048): Use a validation method that permits more accurate,
+        // but still inexpensive, checking of likely valid DNS hostnames.
+        final String weakHostnameRegex = "^[a-zA-Z0-9_.-]+$";
+        if (!hostname.matches(weakHostnameRegex)) {
+            return false;
+        }
+
+        for (int address_family : ADDRESS_FAMILIES) {
+            if (Os.inet_pton(address_family, hostname) != null) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Safely multiple a value by a rational.
+     * <p>
+     * Internally it uses integer-based math whenever possible, but switches
+     * over to double-based math if values would overflow.
+     * @hide
+     */
+    public static long multiplySafeByRational(long value, long num, long den) {
+        if (den == 0) {
+            throw new ArithmeticException("Invalid Denominator");
+        }
+        long x = value;
+        long y = num;
+
+        // Logic shamelessly borrowed from Math.multiplyExact()
+        long r = x * y;
+        long ax = Math.abs(x);
+        long ay = Math.abs(y);
+        if (((ax | ay) >>> 31 != 0)) {
+            // Some bits greater than 2^31 that might cause overflow
+            // Check the result using the divide operator
+            // and check for the special case of Long.MIN_VALUE * -1
+            if (((y != 0) && (r / y != x))
+                    || (x == Long.MIN_VALUE && y == -1)) {
+                // Use double math to avoid overflowing
+                return (long) (((double) num / den) * value);
+            }
+        }
+        return r / den;
+    }
+}
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index e595db3..c416814 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -78,13 +78,16 @@
         boolean useSystemSuspend = (new File(sSysClassWakeupDir)).exists();
 
         if (useSystemSuspend) {
-            // Get both kernel and native wakelock stats from SystemSuspend
-            updateVersion(staleStats);
-            if (getWakelockStatsFromSystemSuspend(staleStats) == null) {
-                Slog.w(TAG, "Failed to get wakelock stats from SystemSuspend");
-                return null;
+            // static read/write lock protection for sKernelWakelockUpdateVersion
+            synchronized (KernelWakelockReader.class) {
+                // Get both kernel and native wakelock stats from SystemSuspend
+                updateVersion(staleStats);
+                if (getWakelockStatsFromSystemSuspend(staleStats) == null) {
+                    Slog.w(TAG, "Failed to get wakelock stats from SystemSuspend");
+                    return null;
+                }
+                return removeOldStats(staleStats);
             }
-            return removeOldStats(staleStats);
         } else {
             Arrays.fill(mKernelWakelockBuffer, (byte) 0);
             int len = 0;
@@ -141,14 +144,17 @@
                 }
             }
 
-            updateVersion(staleStats);
-            // Get native wakelock stats from SystemSuspend
-            if (getWakelockStatsFromSystemSuspend(staleStats) == null) {
-                Slog.w(TAG, "Failed to get Native wakelock stats from SystemSuspend");
+            // static read/write lock protection for sKernelWakelockUpdateVersion
+            synchronized (KernelWakelockReader.class) {
+                updateVersion(staleStats);
+                // Get native wakelock stats from SystemSuspend
+                if (getWakelockStatsFromSystemSuspend(staleStats) == null) {
+                    Slog.w(TAG, "Failed to get Native wakelock stats from SystemSuspend");
+                }
+                // Get kernel wakelock stats
+                parseProcWakelocks(mKernelWakelockBuffer, len, wakeup_sources, staleStats);
+                return removeOldStats(staleStats);
             }
-            // Get kernel wakelock stats
-            parseProcWakelocks(mKernelWakelockBuffer, len, wakeup_sources, staleStats);
-            return removeOldStats(staleStats);
         }
     }
 
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 15d4525..0381a75 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -24,7 +24,6 @@
 import android.net.Credentials;
 import android.net.LocalServerSocket;
 import android.net.LocalSocket;
-import android.net.NetworkUtils;
 import android.os.FactoryTest;
 import android.os.IVold;
 import android.os.Process;
@@ -35,6 +34,8 @@
 import android.system.Os;
 import android.util.Log;
 
+import com.android.internal.net.NetworkUtilsInternal;
+
 import dalvik.annotation.optimization.FastNative;
 import dalvik.system.ZygoteHooks;
 
@@ -352,7 +353,7 @@
 
             // If no GIDs were specified, don't make any permissions changes based on groups.
             if (gids != null && gids.length > 0) {
-                NetworkUtils.setAllowNetworkingForProcess(containsInetGid(gids));
+                NetworkUtilsInternal.setAllowNetworkingForProcess(containsInetGid(gids));
             }
         }
 
diff --git a/core/java/com/android/internal/util/OWNERS b/core/java/com/android/internal/util/OWNERS
index 8b9acd3..a045451 100644
--- a/core/java/com/android/internal/util/OWNERS
+++ b/core/java/com/android/internal/util/OWNERS
@@ -2,3 +2,4 @@
 per-file MessageUtils*, Protocol*, RingBuffer*, TokenBucket* = jchalard@google.com, lorenzo@google.com, satk@google.com
 per-file Protocol* = etancohen@google.com, lorenzo@google.com
 per-file State* = jchalard@google.com, lorenzo@google.com, satk@google.com
+per-file DataClass* = eugenesusla@google.com
\ No newline at end of file
diff --git a/core/java/com/android/server/net/BaseNetworkObserver.java b/core/java/com/android/server/net/BaseNetworkObserver.java
index 2a9c0b4..0e8d61a 100644
--- a/core/java/com/android/server/net/BaseNetworkObserver.java
+++ b/core/java/com/android/server/net/BaseNetworkObserver.java
@@ -64,7 +64,8 @@
     }
 
     @Override
-    public void interfaceClassDataActivityChanged(String label, boolean active, long tsNanos) {
+    public void interfaceClassDataActivityChanged(String label, boolean active, long tsNanos,
+            int uid) {
         // default no-op
     }
 
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 1968146..b4572fd 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -182,6 +182,7 @@
                 "android_content_res_Configuration.cpp",
                 "android_security_Scrypt.cpp",
                 "com_android_internal_content_om_OverlayConfig.cpp",
+                "com_android_internal_net_NetworkUtilsInternal.cpp",
                 "com_android_internal_os_ClassLoaderFactory.cpp",
                 "com_android_internal_os_FuseAppLoop.cpp",
                 "com_android_internal_os_KernelCpuUidBpfMapReader.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 7b708ef..3198cb1 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -187,6 +187,7 @@
 extern int register_android_security_Scrypt(JNIEnv *env);
 extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env);
 extern int register_com_android_internal_content_om_OverlayConfig(JNIEnv *env);
+extern int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv* env);
 extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
 extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
 extern int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env);
@@ -1520,6 +1521,7 @@
         REG_JNI(register_android_os_SharedMemory),
         REG_JNI(register_android_os_incremental_IncrementalManager),
         REG_JNI(register_com_android_internal_content_om_OverlayConfig),
+        REG_JNI(register_com_android_internal_net_NetworkUtilsInternal),
         REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
         REG_JNI(register_com_android_internal_os_Zygote),
         REG_JNI(register_com_android_internal_os_ZygoteInit),
diff --git a/core/jni/android_media_AudioDeviceAttributes.cpp b/core/jni/android_media_AudioDeviceAttributes.cpp
index e79c95e..2a16dce 100644
--- a/core/jni/android_media_AudioDeviceAttributes.cpp
+++ b/core/jni/android_media_AudioDeviceAttributes.cpp
@@ -31,7 +31,7 @@
                                  const AudioDeviceTypeAddr *devTypeAddr) {
     jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
     jint jNativeType = (jint)devTypeAddr->mType;
-    ScopedLocalRef<jstring> jAddress(env, env->NewStringUTF(devTypeAddr->mAddress.data()));
+    ScopedLocalRef<jstring> jAddress(env, env->NewStringUTF(devTypeAddr->getAddress()));
 
     *jAudioDeviceAttributes = env->NewObject(gAudioDeviceAttributesClass, gAudioDeviceAttributesCstor,
             jNativeType, jAddress.get());
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 7493e39..20e64ff 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -172,6 +172,8 @@
     jmethodID postRecordConfigEventFromNative;
 } gAudioPolicyEventHandlerMethods;
 
+static struct { jmethodID add; } gListMethods;
+
 //
 // JNI Initialization for OpenSLES routing
 //
@@ -292,25 +294,30 @@
     return old;
 }
 
-#define check_AudioSystem_Command(status) _check_AudioSystem_Command(__func__, (status))
+#define check_AudioSystem_Command(...) _check_AudioSystem_Command(__func__, __VA_ARGS__)
 
-static int _check_AudioSystem_Command(const char* caller, status_t status)
-{
-    ALOGE_IF(status, "Command failed for %s: %d", caller, status);
+static int _check_AudioSystem_Command(const char *caller, status_t status,
+                                      std::vector<status_t> ignoredErrors = {}) {
+    int jniStatus = kAudioStatusOk;
     switch (status) {
     case DEAD_OBJECT:
-        return kAudioStatusMediaServerDied;
+        jniStatus = kAudioStatusMediaServerDied;
+        break;
     case NO_ERROR:
-        return kAudioStatusOk;
+        break;
     default:
+        if (std::find(begin(ignoredErrors), end(ignoredErrors), status) == end(ignoredErrors)) {
+            jniStatus = kAudioStatusError;
+        }
         break;
     }
-    return kAudioStatusError;
+    ALOGE_IF(jniStatus != kAudioStatusOk, "Command failed for %s: %d", caller, status);
+    return jniStatus;
 }
 
 static jint getVectorOfAudioDeviceTypeAddr(JNIEnv *env, jintArray deviceTypes,
                                            jobjectArray deviceAddresses,
-                                           Vector<AudioDeviceTypeAddr> &audioDeviceTypeAddrVector) {
+                                           AudioDeviceTypeAddrVector &audioDeviceTypeAddrVector) {
     if (deviceTypes == nullptr || deviceAddresses == nullptr) {
         return (jint)AUDIO_JAVA_BAD_VALUE;
     }
@@ -337,7 +344,7 @@
         }
         const char *address = env->GetStringUTFChars((jstring)addrJobj, NULL);
         AudioDeviceTypeAddr dev = AudioDeviceTypeAddr((audio_devices_t)typesPtr[i], address);
-        audioDeviceTypeAddrVector.add(dev);
+        audioDeviceTypeAddrVector.push_back(dev);
         env->ReleaseStringUTFChars((jstring)addrJobj, address);
     }
     env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0);
@@ -2063,7 +2070,7 @@
 
 static jint android_media_AudioSystem_setUidDeviceAffinities(JNIEnv *env, jobject clazz,
         jint uid, jintArray deviceTypes, jobjectArray deviceAddresses) {
-    Vector<AudioDeviceTypeAddr> deviceVector;
+    AudioDeviceTypeAddrVector deviceVector;
     jint results = getVectorOfAudioDeviceTypeAddr(env, deviceTypes, deviceAddresses, deviceVector);
     if (results != NO_ERROR) {
         return results;
@@ -2081,7 +2088,7 @@
 static jint android_media_AudioSystem_setUserIdDeviceAffinities(JNIEnv *env, jobject clazz,
                                                                 jint userId, jintArray deviceTypes,
                                                                 jobjectArray deviceAddresses) {
-    Vector<AudioDeviceTypeAddr> deviceVector;
+    AudioDeviceTypeAddrVector deviceVector;
     jint results = getVectorOfAudioDeviceTypeAddr(env, deviceTypes, deviceAddresses, deviceVector);
     if (results != NO_ERROR) {
         return results;
@@ -2362,48 +2369,124 @@
     return AudioSystem::isCallScreenModeSupported();
 }
 
-static jint
-android_media_AudioSystem_setPreferredDeviceForStrategy(JNIEnv *env, jobject thiz,
-        jint strategy, jint deviceType, jstring deviceAddress) {
-
-    const char *c_address = env->GetStringUTFChars(deviceAddress, NULL);
+static jint android_media_AudioSystem_setDevicesRoleForStrategy(JNIEnv *env, jobject thiz,
+                                                                jint strategy, jint role,
+                                                                jintArray jDeviceTypes,
+                                                                jobjectArray jDeviceAddresses) {
+    AudioDeviceTypeAddrVector nDevices;
+    jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices);
+    if (results != NO_ERROR) {
+        return results;
+    }
     int status = check_AudioSystem_Command(
-            AudioSystem::setPreferredDeviceForStrategy((product_strategy_t)strategy,
-                                                       AudioDeviceTypeAddr((audio_devices_t)
-                                                                                   deviceType,
-                                                                           c_address)));
-    env->ReleaseStringUTFChars(deviceAddress, c_address);
+            AudioSystem::setDevicesRoleForStrategy((product_strategy_t)strategy,
+                                                   (device_role_t)role, nDevices));
     return (jint) status;
 }
 
-static jint
-android_media_AudioSystem_removePreferredDeviceForStrategy(JNIEnv *env, jobject thiz, jint strategy)
-{
-    return (jint) check_AudioSystem_Command(
-            AudioSystem::removePreferredDeviceForStrategy((product_strategy_t) strategy));
+static jint android_media_AudioSystem_removeDevicesRoleForStrategy(JNIEnv *env, jobject thiz,
+                                                                   jint strategy, jint role) {
+    return (jint)
+            check_AudioSystem_Command(AudioSystem::removeDevicesRoleForStrategy((product_strategy_t)
+                                                                                        strategy,
+                                                                                (device_role_t)
+                                                                                        role),
+                                      {NAME_NOT_FOUND});
 }
 
-static jint
-android_media_AudioSystem_getPreferredDeviceForStrategy(JNIEnv *env, jobject thiz,
-        jint strategy, jobjectArray jDeviceArray)
-{
-    if (jDeviceArray == nullptr || env->GetArrayLength(jDeviceArray) != 1) {
-        ALOGE("%s invalid array to store AudioDeviceAttributes", __FUNCTION__);
-        return (jint)AUDIO_JAVA_BAD_VALUE;
-    }
-
-    AudioDeviceTypeAddr elDevice;
+static jint android_media_AudioSystem_getDevicesForRoleAndStrategy(JNIEnv *env, jobject thiz,
+                                                                   jint strategy, jint role,
+                                                                   jobject jDevices) {
+    AudioDeviceTypeAddrVector nDevices;
     status_t status = check_AudioSystem_Command(
-            AudioSystem::getPreferredDeviceForStrategy((product_strategy_t) strategy, elDevice));
+            AudioSystem::getDevicesForRoleAndStrategy((product_strategy_t)strategy,
+                                                      (device_role_t)role, nDevices));
     if (status != NO_ERROR) {
         return (jint) status;
     }
-    jobject jAudioDeviceAttributes = NULL;
-    jint jStatus = createAudioDeviceAttributesFromNative(env, &jAudioDeviceAttributes, &elDevice);
-    if (jStatus == AUDIO_JAVA_SUCCESS) {
-        env->SetObjectArrayElement(jDeviceArray, 0, jAudioDeviceAttributes);
+    for (const auto &device : nDevices) {
+        jobject jAudioDeviceAttributes = NULL;
+        jint jStatus = createAudioDeviceAttributesFromNative(env, &jAudioDeviceAttributes, &device);
+        if (jStatus != AUDIO_JAVA_SUCCESS) {
+            return jStatus;
+        }
+        env->CallBooleanMethod(jDevices, gListMethods.add, jAudioDeviceAttributes);
+        env->DeleteLocalRef(jAudioDeviceAttributes);
     }
-    return jStatus;
+    return AUDIO_JAVA_SUCCESS;
+}
+
+static jint android_media_AudioSystem_setDevicesRoleForCapturePreset(
+        JNIEnv *env, jobject thiz, jint capturePreset, jint role, jintArray jDeviceTypes,
+        jobjectArray jDeviceAddresses) {
+    AudioDeviceTypeAddrVector nDevices;
+    jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices);
+    if (results != NO_ERROR) {
+        return results;
+    }
+    int status = check_AudioSystem_Command(
+            AudioSystem::setDevicesRoleForCapturePreset((audio_source_t)capturePreset,
+                                                        (device_role_t)role, nDevices));
+    return (jint)status;
+}
+
+static jint android_media_AudioSystem_addDevicesRoleForCapturePreset(
+        JNIEnv *env, jobject thiz, jint capturePreset, jint role, jintArray jDeviceTypes,
+        jobjectArray jDeviceAddresses) {
+    AudioDeviceTypeAddrVector nDevices;
+    jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices);
+    if (results != NO_ERROR) {
+        return results;
+    }
+    int status = check_AudioSystem_Command(
+            AudioSystem::addDevicesRoleForCapturePreset((audio_source_t)capturePreset,
+                                                        (device_role_t)role, nDevices));
+    return (jint)status;
+}
+
+static jint android_media_AudioSystem_removeDevicesRoleForCapturePreset(
+        JNIEnv *env, jobject thiz, jint capturePreset, jint role, jintArray jDeviceTypes,
+        jobjectArray jDeviceAddresses) {
+    AudioDeviceTypeAddrVector nDevices;
+    jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices);
+    if (results != NO_ERROR) {
+        return results;
+    }
+    int status = check_AudioSystem_Command(
+            AudioSystem::removeDevicesRoleForCapturePreset((audio_source_t)capturePreset,
+                                                           (device_role_t)role, nDevices));
+    return (jint)status;
+}
+
+static jint android_media_AudioSystem_clearDevicesRoleForCapturePreset(JNIEnv *env, jobject thiz,
+                                                                       jint capturePreset,
+                                                                       jint role) {
+    return (jint)check_AudioSystem_Command(
+            AudioSystem::clearDevicesRoleForCapturePreset((audio_source_t)capturePreset,
+                                                          (device_role_t)role));
+}
+
+static jint android_media_AudioSystem_getDevicesForRoleAndCapturePreset(JNIEnv *env, jobject thiz,
+                                                                        jint capturePreset,
+                                                                        jint role,
+                                                                        jobject jDevices) {
+    AudioDeviceTypeAddrVector nDevices;
+    status_t status = check_AudioSystem_Command(
+            AudioSystem::getDevicesForRoleAndCapturePreset((audio_source_t)capturePreset,
+                                                           (device_role_t)role, nDevices));
+    if (status != NO_ERROR) {
+        return (jint)status;
+    }
+    for (const auto &device : nDevices) {
+        jobject jAudioDeviceAttributes = NULL;
+        jint jStatus = createAudioDeviceAttributesFromNative(env, &jAudioDeviceAttributes, &device);
+        if (jStatus != AUDIO_JAVA_SUCCESS) {
+            return jStatus;
+        }
+        env->CallBooleanMethod(jDevices, gListMethods.add, jAudioDeviceAttributes);
+        env->DeleteLocalRef(jAudioDeviceAttributes);
+    }
+    return AUDIO_JAVA_SUCCESS;
 }
 
 static jint
@@ -2551,12 +2634,22 @@
          {"setAudioHalPids", "([I)I", (void *)android_media_AudioSystem_setAudioHalPids},
          {"isCallScreeningModeSupported", "()Z",
           (void *)android_media_AudioSystem_isCallScreeningModeSupported},
-         {"setPreferredDeviceForStrategy", "(IILjava/lang/String;)I",
-          (void *)android_media_AudioSystem_setPreferredDeviceForStrategy},
-         {"removePreferredDeviceForStrategy", "(I)I",
-          (void *)android_media_AudioSystem_removePreferredDeviceForStrategy},
-         {"getPreferredDeviceForStrategy", "(I[Landroid/media/AudioDeviceAttributes;)I",
-          (void *)android_media_AudioSystem_getPreferredDeviceForStrategy},
+         {"setDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I",
+          (void *)android_media_AudioSystem_setDevicesRoleForStrategy},
+         {"removeDevicesRoleForStrategy", "(II)I",
+          (void *)android_media_AudioSystem_removeDevicesRoleForStrategy},
+         {"getDevicesForRoleAndStrategy", "(IILjava/util/List;)I",
+          (void *)android_media_AudioSystem_getDevicesForRoleAndStrategy},
+         {"setDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
+          (void *)android_media_AudioSystem_setDevicesRoleForCapturePreset},
+         {"addDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
+          (void *)android_media_AudioSystem_addDevicesRoleForCapturePreset},
+         {"removeDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
+          (void *)android_media_AudioSystem_removeDevicesRoleForCapturePreset},
+         {"clearDevicesRoleForCapturePreset", "(II)I",
+          (void *)android_media_AudioSystem_clearDevicesRoleForCapturePreset},
+         {"getDevicesForRoleAndCapturePreset", "(IILjava/util/List;)I",
+          (void *)android_media_AudioSystem_getDevicesForRoleAndCapturePreset},
          {"getDevicesForAttributes",
           "(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAttributes;)I",
           (void *)android_media_AudioSystem_getDevicesForAttributes},
@@ -2758,6 +2851,9 @@
     gMidAudioRecordRoutingProxy_release =
             android::GetMethodIDOrDie(env, gClsAudioRecordRoutingProxy, "native_release", "()V");
 
+    jclass listClass = FindClassOrDie(env, "java/util/List");
+    gListMethods.add = GetMethodIDOrDie(env, listClass, "add", "(Ljava/lang/Object;)Z");
+
     AudioSystem::addErrorCallback(android_media_AudioSystem_error_callback);
 
     RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 8d4c4e5..2155246 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2008, The Android Open Source Project
+ * Copyright 2020, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -28,7 +28,6 @@
 #include <netinet/udp.h>
 
 #include <DnsProxydProtocol.h> // NETID_USE_LOCAL_NAMESERVERS
-#include <android_runtime/AndroidRuntime.h>
 #include <cutils/properties.h>
 #include <nativehelper/JNIPlatformHelp.h>
 #include <nativehelper/ScopedLocalRef.h>
@@ -226,11 +225,6 @@
             class_Network, ctor, dnsNetId & ~NETID_USE_LOCAL_NAMESERVERS, privateDnsBypass);
 }
 
-static void android_net_utils_setAllowNetworkingForProcess(JNIEnv *env, jobject thiz,
-                                                           jboolean hasConnectivity) {
-    setAllowNetworkingForProcess(hasConnectivity == JNI_TRUE);
-}
-
 static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) {
     if (javaFd == NULL) {
         jniThrowNullPointerException(env, NULL);
@@ -288,7 +282,6 @@
     { "resNetworkResult", "(Ljava/io/FileDescriptor;)Landroid/net/DnsResolver$DnsResponse;", (void*) android_net_utils_resNetworkResult },
     { "resNetworkCancel", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_resNetworkCancel },
     { "getDnsNetwork", "()Landroid/net/Network;", (void*) android_net_utils_getDnsNetwork },
-    { "setAllowNetworkingForProcess", "(Z)V", (void *)android_net_utils_setAllowNetworkingForProcess },
 };
 // clang-format on
 
diff --git a/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp b/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp
new file mode 100644
index 0000000..10fc18d
--- /dev/null
+++ b/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NetdClient.h"
+#include "core_jni_helpers.h"
+#include "jni.h"
+
+namespace android {
+static void android_net_utils_setAllowNetworkingForProcess(JNIEnv *env, jobject thiz,
+                                                           jboolean hasConnectivity) {
+    setAllowNetworkingForProcess(hasConnectivity == JNI_TRUE);
+}
+
+static const JNINativeMethod gNetworkUtilMethods[] = {
+        {"setAllowNetworkingForProcess", "(Z)V",
+         (void *)android_net_utils_setAllowNetworkingForProcess},
+};
+
+int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv *env) {
+    return RegisterMethodsOrDie(env, "com/android/internal/net/NetworkUtilsInternal",
+                                gNetworkUtilMethods, NELEM(gNetworkUtilMethods));
+}
+
+} // namespace android
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
index 83ff84d..56f7ea8 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
@@ -309,7 +309,7 @@
         if (parameters != null) {
             for (KeyParameter p : parameters) {
                 if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
-                    returnedIv = p.blob;
+                    returnedIv = p.value.getBlob();
                     break;
                 }
             }
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
index aab84e3..64da837 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
@@ -330,7 +330,7 @@
         if (parameters != null) {
             for (KeyParameter p : parameters) {
                 if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
-                    returnedIv = p.blob;
+                    returnedIv = p.value.getBlob();
                     break;
                 }
             }
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 176507c..403da18 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -366,10 +366,10 @@
         for (Authorization a : response.metadata.authorizations) {
             switch (a.keyParameter.tag) {
                 case KeymasterDefs.KM_TAG_ALGORITHM:
-                    keymasterAlgorithm = a.keyParameter.integer;
+                    keymasterAlgorithm = a.keyParameter.value.getAlgorithm();
                     break;
                 case KeymasterDefs.KM_TAG_DIGEST:
-                    if (keymasterDigest == -1) keymasterDigest = a.keyParameter.integer;
+                    if (keymasterDigest == -1) keymasterDigest = a.keyParameter.value.getDigest();
                     break;
             }
         }
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
index 9d3b970..74503e1 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -102,7 +102,8 @@
                         insideSecureHardware =
                                 KeyStore2ParameterUtils.isSecureHardware(a.securityLevel);
                         securityLevel = a.securityLevel;
-                        origin = KeyProperties.Origin.fromKeymaster(a.keyParameter.integer);
+                        origin = KeyProperties.Origin.fromKeymaster(
+                                a.keyParameter.value.getOrigin());
                         break;
                     case KeymasterDefs.KM_TAG_KEY_SIZE:
                         long keySizeUnsigned = KeyStore2ParameterUtils.getUnsignedInt(a);
@@ -113,45 +114,51 @@
                         keySize = (int) keySizeUnsigned;
                         break;
                     case KeymasterDefs.KM_TAG_PURPOSE:
-                        purposes |= KeyProperties.Purpose.fromKeymaster(a.keyParameter.integer);
+                        purposes |= KeyProperties.Purpose.fromKeymaster(
+                                a.keyParameter.value.getKeyPurpose());
                         break;
                     case KeymasterDefs.KM_TAG_PADDING:
+                        int paddingMode = a.keyParameter.value.getPaddingMode();
                         try {
-                            if (a.keyParameter.integer == KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN
-                                    || a.keyParameter.integer == KeymasterDefs.KM_PAD_RSA_PSS) {
+                            if (paddingMode == KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN
+                                    || paddingMode == KeymasterDefs.KM_PAD_RSA_PSS) {
                                 @KeyProperties.SignaturePaddingEnum String padding =
                                         KeyProperties.SignaturePadding.fromKeymaster(
-                                                a.keyParameter.integer);
+                                                paddingMode);
                                 signaturePaddingsList.add(padding);
                             } else {
                                 @KeyProperties.EncryptionPaddingEnum String jcaPadding =
                                         KeyProperties.EncryptionPadding.fromKeymaster(
-                                                a.keyParameter.integer);
+                                                paddingMode);
                                 encryptionPaddingsList.add(jcaPadding);
                             }
                         } catch (IllegalArgumentException e) {
                             throw new ProviderException("Unsupported padding: "
-                                                + a.keyParameter.integer);
+                                                + paddingMode);
                         }
                         break;
                     case KeymasterDefs.KM_TAG_DIGEST:
-                        digestsList.add(KeyProperties.Digest.fromKeymaster(a.keyParameter.integer));
+                        digestsList.add(KeyProperties.Digest.fromKeymaster(
+                                a.keyParameter.value.getDigest()));
                         break;
                     case KeymasterDefs.KM_TAG_BLOCK_MODE:
                         blockModesList.add(
-                                KeyProperties.BlockMode.fromKeymaster(a.keyParameter.integer)
+                                KeyProperties.BlockMode.fromKeymaster(
+                                        a.keyParameter.value.getBlockMode())
                         );
                         break;
                     case KeymasterDefs.KM_TAG_USER_AUTH_TYPE:
+                        int authenticatorType = a.keyParameter.value.getHardwareAuthenticatorType();
                         if (KeyStore2ParameterUtils.isSecureHardware(a.securityLevel)) {
-                            keymasterHwEnforcedUserAuthenticators = a.keyParameter.integer;
+                            keymasterHwEnforcedUserAuthenticators = authenticatorType;
                         } else {
-                            keymasterSwEnforcedUserAuthenticators = a.keyParameter.integer;
+                            keymasterSwEnforcedUserAuthenticators = authenticatorType;
                         }
                         break;
                     case KeymasterDefs.KM_TAG_USER_SECURE_ID:
                         keymasterSecureUserIds.add(
-                                KeymasterArguments.toUint64(a.keyParameter.longInteger));
+                                KeymasterArguments.toUint64(
+                                        a.keyParameter.value.getLongInteger()));
                         break;
                     case KeymasterDefs.KM_TAG_ACTIVE_DATETIME:
                         keyValidityStart = KeyStore2ParameterUtils.getDate(a);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
index fd3d289..5c048a1 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
@@ -300,7 +300,7 @@
         if (parameters != null) {
             for (KeyParameter p : parameters) {
                 if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
-                    returnedIv = p.blob;
+                    returnedIv = p.value.getBlob();
                     break;
                 }
             }
diff --git a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
index ae2e475..4c8ab8d 100644
--- a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
@@ -19,7 +19,9 @@
 import android.annotation.NonNull;
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameterValue;
 import android.hardware.security.keymint.SecurityLevel;
+import android.hardware.security.keymint.Tag;
 import android.security.GateKeeper;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.KeyProperties;
@@ -50,7 +52,7 @@
         }
         KeyParameter p = new KeyParameter();
         p.tag = tag;
-        p.boolValue = true;
+        p.value = KeyParameterValue.boolValue(true);
         return p;
     }
 
@@ -62,14 +64,40 @@
      * @hide
      */
     static @NonNull KeyParameter makeEnum(int tag, int v) {
-        int type = KeymasterDefs.getTagType(tag);
-        if (type != KeymasterDefs.KM_ENUM && type != KeymasterDefs.KM_ENUM_REP) {
-            throw new IllegalArgumentException("Not an enum or repeatable enum tag: " + tag);
+        KeyParameter kp = new KeyParameter();
+        kp.tag = tag;
+        switch (tag) {
+            case Tag.PURPOSE:
+                kp.value = KeyParameterValue.keyPurpose(v);
+                break;
+            case Tag.ALGORITHM:
+                kp.value = KeyParameterValue.algorithm(v);
+                break;
+            case Tag.BLOCK_MODE:
+                kp.value = KeyParameterValue.blockMode(v);
+                break;
+            case Tag.DIGEST:
+                kp.value = KeyParameterValue.digest(v);
+                break;
+            case Tag.EC_CURVE:
+                kp.value = KeyParameterValue.ecCurve(v);
+                break;
+            case Tag.ORIGIN:
+                kp.value = KeyParameterValue.origin(v);
+                break;
+            case Tag.PADDING:
+                kp.value = KeyParameterValue.paddingMode(v);
+                break;
+            case Tag.USER_AUTH_TYPE:
+                kp.value = KeyParameterValue.hardwareAuthenticatorType(v);
+                break;
+            case Tag.HARDWARE_TYPE:
+                kp.value = KeyParameterValue.securityLevel(v);
+                break;
+            default:
+                throw new IllegalArgumentException("Not an enum or repeatable enum tag: " + tag);
         }
-        KeyParameter p = new KeyParameter();
-        p.tag = tag;
-        p.integer = v;
-        return p;
+        return kp;
     }
 
     /**
@@ -86,7 +114,7 @@
         }
         KeyParameter p = new KeyParameter();
         p.tag = tag;
-        p.integer = v;
+        p.value = KeyParameterValue.integer(v);
         return p;
     }
 
@@ -104,7 +132,7 @@
         }
         KeyParameter p = new KeyParameter();
         p.tag = tag;
-        p.longInteger = v;
+        p.value = KeyParameterValue.longInteger(v);
         return p;
     }
 
@@ -121,7 +149,7 @@
         }
         KeyParameter p = new KeyParameter();
         p.tag = tag;
-        p.blob = b;
+        p.value = KeyParameterValue.blob(b);
         return p;
     }
 
@@ -138,9 +166,10 @@
         }
         KeyParameter p = new KeyParameter();
         p.tag = tag;
-        p.longInteger = date.getTime();
-        if (p.longInteger < 0) {
-            throw new IllegalArgumentException("Date tag value out of range: " + p.longInteger);
+        p.value = KeyParameterValue.dateTime(date.getTime());
+        if (p.value.getDateTime() < 0) {
+            throw new IllegalArgumentException("Date tag value out of range: "
+                    + p.value.getDateTime());
         }
         return p;
     }
@@ -160,18 +189,18 @@
             throw new IllegalArgumentException("Not an int tag: " + param.keyParameter.tag);
         }
         // KM_UINT is 32 bits wide so we must suppress sign extension.
-        return ((long) param.keyParameter.integer) & 0xffffffffL;
+        return ((long) param.keyParameter.value.getInteger()) & 0xffffffffL;
     }
 
     static @NonNull Date getDate(@NonNull Authorization param) {
         if (KeymasterDefs.getTagType(param.keyParameter.tag) != KeymasterDefs.KM_DATE) {
             throw new IllegalArgumentException("Not a date tag: " + param.keyParameter.tag);
         }
-        if (param.keyParameter.longInteger < 0) {
+        if (param.keyParameter.value.getDateTime() < 0) {
             throw new IllegalArgumentException("Date Value too large: "
-                    + param.keyParameter.longInteger);
+                    + param.keyParameter.value.getDateTime());
         }
-        return new Date(param.keyParameter.longInteger);
+        return new Date(param.keyParameter.value.getDateTime());
     }
 
     static void forEachSetFlag(int flags, Consumer<Integer> consumer) {
diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
index 3b11854..f87a3d2 100644
--- a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
@@ -57,7 +57,7 @@
         for (Authorization p : key.getAuthorizations()) {
             switch(p.keyParameter.tag) {
                 case KeymasterDefs.KM_TAG_USER_SECURE_ID:
-                    keySids.add(p.keyParameter.longInteger);
+                    keySids.add(p.keyParameter.value.getLongInteger());
                     break;
                 default:
                     break;
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index a545b3d..bec80a7 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -670,7 +670,7 @@
     }
 
     auto entry_flags = type_spec->GetFlagsForEntryIndex(entry_idx);
-    if (UNLIKELY(!entry_flags)) {
+    if (UNLIKELY(!entry_flags.has_value())) {
       return base::unexpected(entry_flags.error());
     }
     type_flags |= entry_flags.value();
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 4010e4e..bce70e2 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -897,12 +897,12 @@
             // Decode the UTF-16 length. This is not used if we're not
             // converting to UTF-16 from UTF-8.
             const base::expected<size_t, IOError> u16len = decodeLength(&str);
-            if (UNLIKELY(!u16len)) {
+            if (UNLIKELY(!u16len.has_value())) {
                 return base::unexpected(u16len.error());
             }
 
             const base::expected<size_t, IOError> u8len = decodeLength(&str);
-            if (UNLIKELY(!u8len)) {
+            if (UNLIKELY(!u8len.has_value())) {
                 return base::unexpected(u8len.error());
             }
 
diff --git a/libs/androidfw/ResourceUtils.cpp b/libs/androidfw/ResourceUtils.cpp
index a34aa72..87fb2c0 100644
--- a/libs/androidfw/ResourceUtils.cpp
+++ b/libs/androidfw/ResourceUtils.cpp
@@ -56,7 +56,8 @@
     .package_len = package_name.size(),
   };
 
-  if (base::expected<StringPiece, NullOrIOError> type_str = type_string_ref.string8()) {
+  if (base::expected<StringPiece, NullOrIOError> type_str = type_string_ref.string8();
+      type_str.ok()) {
     name.type = type_str->data();
     name.type_len = type_str->size();
   } else if (UNLIKELY(IsIOError(type_str))) {
@@ -64,7 +65,8 @@
   }
 
   if (name.type == nullptr) {
-    if (base::expected<StringPiece16, NullOrIOError> type16_str = type_string_ref.string16()) {
+    if (base::expected<StringPiece16, NullOrIOError> type16_str = type_string_ref.string16();
+        type16_str.ok()) {
       name.type16 = type16_str->data();
       name.type_len = type16_str->size();
     } else if (!type16_str.has_value()) {
@@ -72,7 +74,8 @@
     }
   }
 
-  if (base::expected<StringPiece, NullOrIOError> entry_str = entry_string_ref.string8()) {
+  if (base::expected<StringPiece, NullOrIOError> entry_str = entry_string_ref.string8();
+      entry_str.ok()) {
     name.entry = entry_str->data();
     name.entry_len = entry_str->size();
   } else if (UNLIKELY(IsIOError(entry_str))) {
@@ -80,7 +83,8 @@
   }
 
   if (name.entry == nullptr) {
-    if (base::expected<StringPiece16, NullOrIOError> entry16_str = entry_string_ref.string16()) {
+    if (base::expected<StringPiece16, NullOrIOError> entry16_str = entry_string_ref.string16();
+        entry16_str.ok()) {
       name.entry16 = entry16_str->data();
       name.entry_len = entry16_str->size();
     } else if (!entry16_str.has_value()) {
diff --git a/media/java/android/media/AudioDeviceAttributes.java b/media/java/android/media/AudioDeviceAttributes.java
index 0ab62c1..7caac89 100644
--- a/media/java/android/media/AudioDeviceAttributes.java
+++ b/media/java/android/media/AudioDeviceAttributes.java
@@ -72,6 +72,11 @@
     private final @Role int mRole;
 
     /**
+     * The internal audio device type
+     */
+    private final int mNativeType;
+
+    /**
      * @hide
      * Constructor from a valid {@link AudioDeviceInfo}
      * @param deviceInfo the connected audio device from which to obtain the device-identifying
@@ -83,6 +88,7 @@
         mRole = deviceInfo.isSink() ? ROLE_OUTPUT : ROLE_INPUT;
         mType = deviceInfo.getType();
         mAddress = deviceInfo.getAddress();
+        mNativeType = deviceInfo.getInternalType();
     }
 
     /**
@@ -101,9 +107,12 @@
         }
         if (role == ROLE_OUTPUT) {
             AudioDeviceInfo.enforceValidAudioDeviceTypeOut(type);
-        }
-        if (role == ROLE_INPUT) {
+            mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(type);
+        } else if (role == ROLE_INPUT) {
             AudioDeviceInfo.enforceValidAudioDeviceTypeIn(type);
+            mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(type);
+        } else {
+            mNativeType = AudioSystem.DEVICE_NONE;
         }
 
         mRole = role;
@@ -111,10 +120,17 @@
         mAddress = address;
     }
 
-    /*package*/ AudioDeviceAttributes(int nativeType, @NonNull String address) {
+    /**
+     * @hide
+     * Constructor from internal device type and address
+     * @param type the internal device type, as defined in {@link AudioSystem}
+     * @param address the address of the device, or an empty string for devices without one
+     */
+    public AudioDeviceAttributes(int nativeType, @NonNull String address) {
         mRole = (nativeType & AudioSystem.DEVICE_BIT_IN) != 0 ? ROLE_INPUT : ROLE_OUTPUT;
         mType = AudioDeviceInfo.convertInternalDeviceToDeviceType(nativeType);
         mAddress = address;
+        mNativeType = nativeType;
     }
 
     /**
@@ -147,6 +163,15 @@
         return mAddress;
     }
 
+    /**
+     * @hide
+     * Returns the internal device type of a device
+     * @return the internal device type
+     */
+    public int getInternalType() {
+        return mNativeType;
+    }
+
     @Override
     public int hashCode() {
         return Objects.hash(mRole, mType, mAddress);
@@ -172,10 +197,8 @@
     public String toString() {
         return new String("AudioDeviceAttributes:"
                 + " role:" + roleToString(mRole)
-                + " type:" + (mRole == ROLE_OUTPUT ? AudioSystem.getOutputDeviceName(
-                        AudioDeviceInfo.convertDeviceTypeToInternalDevice(mType))
-                        : AudioSystem.getInputDeviceName(
-                                AudioDeviceInfo.convertDeviceTypeToInternalDevice(mType)))
+                + " type:" + (mRole == ROLE_OUTPUT ? AudioSystem.getOutputDeviceName(mNativeType)
+                        : AudioSystem.getInputDeviceName(mNativeType))
                 + " addr:" + mAddress);
     }
 
@@ -189,12 +212,14 @@
         dest.writeInt(mRole);
         dest.writeInt(mType);
         dest.writeString(mAddress);
+        dest.writeInt(mNativeType);
     }
 
     private AudioDeviceAttributes(@NonNull Parcel in) {
         mRole = in.readInt();
         mType = in.readInt();
         mAddress = in.readString();
+        mNativeType = in.readInt();
     }
 
     public static final @NonNull Parcelable.Creator<AudioDeviceAttributes> CREATOR =
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index ff4a256..f79fc92 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -350,6 +350,14 @@
     }
 
     /**
+     * @hide
+     * @return the internal device tyoe
+     */
+    public int getInternalType() {
+        return mPort.type();
+    }
+
+    /**
      * @return The internal device ID.
      */
     public int getId() {
@@ -512,10 +520,21 @@
         return INT_TO_EXT_DEVICE_MAPPING.get(intDevice, TYPE_UNKNOWN);
     }
 
+    /** @hide */
+    public static int convertDeviceTypeToInternalInputDevice(int deviceType) {
+        return EXT_TO_INT_INPUT_DEVICE_MAPPING.get(deviceType, AudioSystem.DEVICE_NONE);
+    }
+
     private static final SparseIntArray INT_TO_EXT_DEVICE_MAPPING;
 
     private static final SparseIntArray EXT_TO_INT_DEVICE_MAPPING;
 
+    /**
+     * EXT_TO_INT_INPUT_DEVICE_MAPPING aims at mapping external device type to internal input device
+     * type.
+     */
+    private static final SparseIntArray EXT_TO_INT_INPUT_DEVICE_MAPPING;
+
     static {
         INT_TO_EXT_DEVICE_MAPPING = new SparseIntArray();
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_EARPIECE, TYPE_BUILTIN_EARPIECE);
@@ -600,6 +619,32 @@
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_REMOTE_SUBMIX, AudioSystem.DEVICE_OUT_REMOTE_SUBMIX);
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_OUT_BLE_HEADSET);
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_SPEAKER, AudioSystem.DEVICE_OUT_BLE_SPEAKER);
+
+        // privileges mapping to input device
+        EXT_TO_INT_INPUT_DEVICE_MAPPING = new SparseIntArray();
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BUILTIN_MIC, AudioSystem.DEVICE_IN_BUILTIN_MIC);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+                TYPE_BLUETOOTH_SCO, AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+                TYPE_WIRED_HEADSET, AudioSystem.DEVICE_IN_WIRED_HEADSET);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_HDMI, AudioSystem.DEVICE_IN_HDMI);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_TELEPHONY, AudioSystem.DEVICE_IN_TELEPHONY_RX);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_DOCK, AudioSystem.DEVICE_IN_ANLG_DOCK_HEADSET);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+                TYPE_USB_ACCESSORY, AudioSystem.DEVICE_IN_USB_ACCESSORY);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_USB_DEVICE, AudioSystem.DEVICE_IN_USB_DEVICE);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_USB_HEADSET, AudioSystem.DEVICE_IN_USB_HEADSET);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_FM_TUNER, AudioSystem.DEVICE_IN_FM_TUNER);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_TV_TUNER, AudioSystem.DEVICE_IN_TV_TUNER);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_LINE_ANALOG, AudioSystem.DEVICE_IN_LINE);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_LINE_DIGITAL, AudioSystem.DEVICE_IN_SPDIF);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+                TYPE_BLUETOOTH_A2DP, AudioSystem.DEVICE_IN_BLUETOOTH_A2DP);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_IP, AudioSystem.DEVICE_IN_IP);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BUS, AudioSystem.DEVICE_IN_BUS);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+                TYPE_REMOTE_SUBMIX, AudioSystem.DEVICE_IN_REMOTE_SUBMIX);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_IN_BLE_HEADSET);
     }
 }
 
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 107d656..7dff0c2 100755
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -75,6 +75,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -1598,21 +1599,14 @@
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
     public boolean setPreferredDeviceForStrategy(@NonNull AudioProductStrategy strategy,
             @NonNull AudioDeviceAttributes device) {
-        Objects.requireNonNull(strategy);
-        Objects.requireNonNull(device);
-        try {
-            final int status =
-                    getService().setPreferredDeviceForStrategy(strategy.getId(), device);
-            return status == AudioSystem.SUCCESS;
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return setPreferredDevicesForStrategy(strategy, Arrays.asList(device));
     }
 
     /**
      * @hide
-     * Removes the preferred audio device previously set with
-     * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)}.
+     * Removes the preferred audio device(s) previously set with
+     * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
+     * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}.
      * @param strategy the audio strategy whose routing will be affected
      * @return true if the operation was successful, false otherwise (invalid strategy, or no
      *     device set for example)
@@ -1623,7 +1617,7 @@
         Objects.requireNonNull(strategy);
         try {
             final int status =
-                    getService().removePreferredDeviceForStrategy(strategy.getId());
+                    getService().removePreferredDevicesForStrategy(strategy.getId());
             return status == AudioSystem.SUCCESS;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1633,18 +1627,74 @@
     /**
      * @hide
      * Return the preferred device for an audio strategy, previously set with
+     * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
+     * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
+     * @param strategy the strategy to query
+     * @return the preferred device for that strategy, if multiple devices are set as preferred
+     *    devices, the first one in the list will be returned. Null will be returned if none was
+     *    ever set or if the strategy is invalid
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @Nullable
+    public AudioDeviceAttributes getPreferredDeviceForStrategy(
+            @NonNull AudioProductStrategy strategy) {
+        List<AudioDeviceAttributes> devices = getPreferredDevicesForStrategy(strategy);
+        return devices.isEmpty() ? null : devices.get(0);
+    }
+
+    /**
+     * @hide
+     * Set the preferred devices for a given strategy, i.e. the audio routing to be used by
+     * this audio strategy. Note that the devices may not be available at the time the preferred
+     * devices is set, but it will be used once made available.
+     * <p>Use {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} to cancel setting
+     * this preference for this strategy.</p>
+     * Note that the list of devices is not a list ranked by preference, but a list of one or more
+     * devices used simultaneously to output the same audio signal.
+     * @param strategy the audio strategy whose routing will be affected
+     * @param devices a non-empty list of the audio devices to route to when available
+     * @return true if the operation was successful, false otherwise
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public boolean setPreferredDevicesForStrategy(@NonNull AudioProductStrategy strategy,
+                                                  @NonNull List<AudioDeviceAttributes> devices) {
+        Objects.requireNonNull(strategy);
+        Objects.requireNonNull(devices);
+        if (devices.isEmpty()) {
+            throw new IllegalArgumentException(
+                    "Tried to set preferred devices for strategy with a empty list");
+        }
+        for (AudioDeviceAttributes device : devices) {
+            Objects.requireNonNull(device);
+        }
+        try {
+            final int status =
+                    getService().setPreferredDevicesForStrategy(strategy.getId(), devices);
+            return status == AudioSystem.SUCCESS;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Return the preferred devices for an audio strategy, previously set with
      * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)}
+     * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
      * @param strategy the strategy to query
      * @return the preferred device for that strategy, or null if none was ever set or if the
      *    strategy is invalid
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
-    public @Nullable AudioDeviceAttributes getPreferredDeviceForStrategy(
+    @NonNull
+    public List<AudioDeviceAttributes> getPreferredDevicesForStrategy(
             @NonNull AudioProductStrategy strategy) {
         Objects.requireNonNull(strategy);
         try {
-            return getService().getPreferredDeviceForStrategy(strategy.getId());
+            return getService().getPreferredDevicesForStrategy(strategy.getId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1656,6 +1706,7 @@
      * strategy.
      * <p>Note that this listener will only be invoked whenever
      * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
+     * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
      * {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} causes a change in
      * preferred device. It will not be invoked directly after registration with
      * {@link #addOnPreferredDeviceForStrategyChangedListener(Executor, OnPreferredDeviceForStrategyChangedListener)}
@@ -1663,8 +1714,10 @@
      * @see #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)
      * @see #removePreferredDeviceForStrategy(AudioProductStrategy)
      * @see #getPreferredDeviceForStrategy(AudioProductStrategy)
+     * @deprecated use #OnPreferredDevicesForStrategyChangedListener
      */
     @SystemApi
+    @Deprecated
     public interface OnPreferredDeviceForStrategyChangedListener {
         /**
          * Called on the listener to indicate that the preferred audio device for the given
@@ -1679,6 +1732,70 @@
 
     /**
      * @hide
+     * Interface to be notified of changes in the preferred audio devices set for a given audio
+     * strategy.
+     * <p>Note that this listener will only be invoked whenever
+     * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
+     * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
+     * {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} causes a change in
+     * preferred device(s). It will not be invoked directly after registration with
+     * {@link #addOnPreferredDevicesForStrategyChangedListener(
+     * Executor, OnPreferredDevicesForStrategyChangedListener)}
+     * to indicate which strategies had preferred devices at the time of registration.</p>
+     * @see #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)
+     * @see #setPreferredDevicesForStrategy(AudioProductStrategy, List)
+     * @see #removePreferredDeviceForStrategy(AudioProductStrategy)
+     * @see #getPreferredDeviceForStrategy(AudioProductStrategy)
+     * @see #getPreferredDevicesForStrategy(AudioProductStrategy)
+     */
+    @SystemApi
+    public interface OnPreferredDevicesForStrategyChangedListener {
+        /**
+         * Called on the listener to indicate that the preferred audio devices for the given
+         * strategy has changed.
+         * @param strategy the {@link AudioProductStrategy} whose preferred device changed
+         * @param devices a list of newly set preferred audio devices
+         */
+        void onPreferredDevicesForStrategyChanged(@NonNull AudioProductStrategy strategy,
+                                                  @NonNull List<AudioDeviceAttributes> devices);
+    }
+
+    /**
+     * @hide
+     * Adds a listener for being notified of changes to the strategy-preferred audio device.
+     * @param executor
+     * @param listener
+     * @throws SecurityException if the caller doesn't hold the required permission
+     * @deprecated use {@link #addOnPreferredDevicesForStrategyChangedListener(
+     *             Executor, AudioManager.OnPreferredDevicesForStrategyChangedListener)} instead
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @Deprecated
+    public void addOnPreferredDeviceForStrategyChangedListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnPreferredDeviceForStrategyChangedListener listener)
+            throws SecurityException {
+        // No-op, the method is deprecated.
+    }
+
+    /**
+     * @hide
+     * Removes a previously added listener of changes to the strategy-preferred audio device.
+     * @param listener
+     * @deprecated use {@link #removeOnPreferredDevicesForStrategyChangedListener(
+     *             AudioManager.OnPreferredDevicesForStrategyChangedListener)} instead
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @Deprecated
+    public void removeOnPreferredDeviceForStrategyChangedListener(
+            @NonNull OnPreferredDeviceForStrategyChangedListener listener) {
+        // No-op, the method is deprecated.
+    }
+
+    /**
+     * @hide
      * Adds a listener for being notified of changes to the strategy-preferred audio device.
      * @param executor
      * @param listener
@@ -1686,16 +1803,16 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
-    public void addOnPreferredDeviceForStrategyChangedListener(
+    public void addOnPreferredDevicesForStrategyChangedListener(
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull OnPreferredDeviceForStrategyChangedListener listener)
+            @NonNull OnPreferredDevicesForStrategyChangedListener listener)
             throws SecurityException {
         Objects.requireNonNull(executor);
         Objects.requireNonNull(listener);
         synchronized (mPrefDevListenerLock) {
             if (hasPrefDevListener(listener)) {
                 throw new IllegalArgumentException(
-                        "attempt to call addOnPreferredDeviceForStrategyChangedListener() "
+                        "attempt to call addOnPreferredDevicesForStrategyChangedListener() "
                                 + "on a previously registered listener");
             }
             // lazy initialization of the list of strategy-preferred device listener
@@ -1707,10 +1824,10 @@
             if (oldCbCount == 0 && mPrefDevListeners.size() > 0) {
                 // register binder for callbacks
                 if (mPrefDevDispatcherStub == null) {
-                    mPrefDevDispatcherStub = new StrategyPreferredDeviceDispatcherStub();
+                    mPrefDevDispatcherStub = new StrategyPreferredDevicesDispatcherStub();
                 }
                 try {
-                    getService().registerStrategyPreferredDeviceDispatcher(mPrefDevDispatcherStub);
+                    getService().registerStrategyPreferredDevicesDispatcher(mPrefDevDispatcherStub);
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
                 }
@@ -1725,8 +1842,8 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
-    public void removeOnPreferredDeviceForStrategyChangedListener(
-            @NonNull OnPreferredDeviceForStrategyChangedListener listener) {
+    public void removeOnPreferredDevicesForStrategyChangedListener(
+            @NonNull OnPreferredDevicesForStrategyChangedListener listener) {
         Objects.requireNonNull(listener);
         synchronized (mPrefDevListenerLock) {
             if (!removePrefDevListener(listener)) {
@@ -1737,7 +1854,7 @@
             if (mPrefDevListeners.size() == 0) {
                 // unregister binder for callbacks
                 try {
-                    getService().unregisterStrategyPreferredDeviceDispatcher(
+                    getService().unregisterStrategyPreferredDevicesDispatcher(
                             mPrefDevDispatcherStub);
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
@@ -1759,23 +1876,23 @@
     private @Nullable ArrayList<PrefDevListenerInfo> mPrefDevListeners;
 
     private static class PrefDevListenerInfo {
-        final @NonNull OnPreferredDeviceForStrategyChangedListener mListener;
+        final @NonNull OnPreferredDevicesForStrategyChangedListener mListener;
         final @NonNull Executor mExecutor;
-        PrefDevListenerInfo(OnPreferredDeviceForStrategyChangedListener listener, Executor exe) {
+        PrefDevListenerInfo(OnPreferredDevicesForStrategyChangedListener listener, Executor exe) {
             mListener = listener;
             mExecutor = exe;
         }
     }
 
     @GuardedBy("mPrefDevListenerLock")
-    private StrategyPreferredDeviceDispatcherStub mPrefDevDispatcherStub;
+    private StrategyPreferredDevicesDispatcherStub mPrefDevDispatcherStub;
 
-    private final class StrategyPreferredDeviceDispatcherStub
-            extends IStrategyPreferredDeviceDispatcher.Stub {
+    private final class StrategyPreferredDevicesDispatcherStub
+            extends IStrategyPreferredDevicesDispatcher.Stub {
 
         @Override
-        public void dispatchPrefDeviceChanged(int strategyId,
-                                              @Nullable AudioDeviceAttributes device) {
+        public void dispatchPrefDevicesChanged(int strategyId,
+                                               @NonNull List<AudioDeviceAttributes> devices) {
             // make a shallow copy of listeners so callback is not executed under lock
             final ArrayList<PrefDevListenerInfo> prefDevListeners;
             synchronized (mPrefDevListenerLock) {
@@ -1790,7 +1907,7 @@
             try {
                 for (PrefDevListenerInfo info : prefDevListeners) {
                     info.mExecutor.execute(() ->
-                            info.mListener.onPreferredDeviceForStrategyChanged(strategy, device));
+                            info.mListener.onPreferredDevicesForStrategyChanged(strategy, devices));
                 }
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -1800,7 +1917,7 @@
 
     @GuardedBy("mPrefDevListenerLock")
     private @Nullable PrefDevListenerInfo getPrefDevListenerInfo(
-            OnPreferredDeviceForStrategyChangedListener listener) {
+            OnPreferredDevicesForStrategyChangedListener listener) {
         if (mPrefDevListeners == null) {
             return null;
         }
@@ -1813,7 +1930,7 @@
     }
 
     @GuardedBy("mPrefDevListenerLock")
-    private boolean hasPrefDevListener(OnPreferredDeviceForStrategyChangedListener listener) {
+    private boolean hasPrefDevListener(OnPreferredDevicesForStrategyChangedListener listener) {
         return getPrefDevListenerInfo(listener) != null;
     }
 
@@ -1821,7 +1938,7 @@
     /**
      * @return true if the listener was removed from the list
      */
-    private boolean removePrefDevListener(OnPreferredDeviceForStrategyChangedListener listener) {
+    private boolean removePrefDevListener(OnPreferredDevicesForStrategyChangedListener listener) {
         final PrefDevListenerInfo infoToRemove = getPrefDevListenerInfo(listener);
         if (infoToRemove != null) {
             mPrefDevListeners.remove(infoToRemove);
@@ -1831,6 +1948,349 @@
     }
 
     //====================================================================
+    // Audio Capture Preset routing
+
+    /**
+     * @hide
+     * Set the preferred device for a given capture preset, i.e. the audio routing to be used by
+     * this capture preset. Note that the device may not be available at the time the preferred
+     * device is set, but it will be used once made available.
+     * <p>Use {@link #clearPreferredDevicesForCapturePreset(int)} to cancel setting this preference
+     * for this capture preset.</p>
+     * @param capturePreset the audio capture preset whose routing will be affected
+     * @param device the audio device to route to when available
+     * @return true if the operation was successful, false otherwise
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public boolean setPreferredDeviceForCapturePreset(int capturePreset,
+                                                      @NonNull AudioDeviceAttributes device) {
+        return setPreferredDevicesForCapturePreset(capturePreset, Arrays.asList(device));
+    }
+
+    /**
+     * @hide
+     * Remove all the preferred audio devices previously set
+     * @param capturePreset the audio capture preset whose routing will be affected
+     * @return true if the operation was successful, false otherwise (invalid capture preset, or no
+     *     device set for example)
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public boolean clearPreferredDevicesForCapturePreset(int capturePreset) {
+        if (!MediaRecorder.isValidAudioSource(capturePreset)) {
+            return false;
+        }
+        try {
+            final int status = getService().clearPreferredDevicesForCapturePreset(capturePreset);
+            return status == AudioSystem.SUCCESS;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Return the preferred devices for an audio capture preset, previously set with
+     * {@link #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)}
+     * @param capturePreset the capture preset to query
+     * @return a list that contains preferred devices for that capture preset.
+     */
+    @NonNull
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int capturePreset) {
+        if (!MediaRecorder.isValidAudioSource(capturePreset)) {
+            return new ArrayList<AudioDeviceAttributes>();
+        }
+        try {
+            return getService().getPreferredDevicesForCapturePreset(capturePreset);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private boolean setPreferredDevicesForCapturePreset(
+            int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
+        Objects.requireNonNull(devices);
+        if (!MediaRecorder.isValidAudioSource(capturePreset)) {
+            return false;
+        }
+        if (devices.size() != 1) {
+            throw new IllegalArgumentException(
+                    "Only support setting one preferred devices for capture preset");
+        }
+        for (AudioDeviceAttributes device : devices) {
+            Objects.requireNonNull(device);
+        }
+        try {
+            final int status =
+                    getService().setPreferredDevicesForCapturePreset(capturePreset, devices);
+            return status == AudioSystem.SUCCESS;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Interface to be notified of changes in the preferred audio devices set for a given capture
+     * preset.
+     * <p>Note that this listener will only be invoked whenever
+     * {@link #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)} or
+     * {@link #clearPreferredDevicesForCapturePreset(int)} causes a change in
+     * preferred device. It will not be invoked directly after registration with
+     * {@link #addOnPreferredDevicesForCapturePresetChangedListener(
+     * Executor, OnPreferredDevicesForCapturePresetChangedListener)}
+     * to indicate which strategies had preferred devices at the time of registration.</p>
+     * @see #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)
+     * @see #clearPreferredDevicesForCapturePreset(int)
+     * @see #getPreferredDevicesForCapturePreset(int)
+     */
+    @SystemApi
+    public interface OnPreferredDevicesForCapturePresetChangedListener {
+        /**
+         * Called on the listener to indicate that the preferred audio devices for the given
+         * capture preset has changed.
+         * @param capturePreset the capture preset whose preferred device changed
+         * @param devices a list of newly set preferred audio devices
+         */
+        void onPreferredDevicesForCapturePresetChanged(
+                int capturePreset, @NonNull List<AudioDeviceAttributes> devices);
+    }
+
+    /**
+     * @hide
+     * Adds a listener for being notified of changes to the capture-preset-preferred audio device.
+     * @param executor
+     * @param listener
+     * @throws SecurityException if the caller doesn't hold the required permission
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public void addOnPreferredDevicesForCapturePresetChangedListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnPreferredDevicesForCapturePresetChangedListener listener)
+            throws SecurityException {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(listener);
+        int status = addOnDevRoleForCapturePresetChangedListener(
+                executor, listener, AudioSystem.DEVICE_ROLE_PREFERRED);
+        if (status == AudioSystem.ERROR) {
+            // This must not happen
+            throw new RuntimeException("Unknown error happened");
+        }
+        if (status == AudioSystem.BAD_VALUE) {
+            throw new IllegalArgumentException(
+                    "attempt to call addOnPreferredDevicesForCapturePresetChangedListener() "
+                            + "on a previously registered listener");
+        }
+    }
+
+    /**
+     * @hide
+     * Removes a previously added listener of changes to the capture-preset-preferred audio device.
+     * @param listener
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public void removeOnPreferredDevicesForCapturePresetChangedListener(
+            @NonNull OnPreferredDevicesForCapturePresetChangedListener listener) {
+        Objects.requireNonNull(listener);
+        int status = removeOnDevRoleForCapturePresetChangedListener(
+                listener, AudioSystem.DEVICE_ROLE_PREFERRED);
+        if (status == AudioSystem.ERROR) {
+            // This must not happen
+            throw new RuntimeException("Unknown error happened");
+        }
+        if (status == AudioSystem.BAD_VALUE) {
+            throw new IllegalArgumentException(
+                    "attempt to call removeOnPreferredDevicesForCapturePresetChangedListener() "
+                            + "on an unregistered listener");
+        }
+    }
+
+    private <T> int addOnDevRoleForCapturePresetChangedListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull T listener, int deviceRole) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(listener);
+        DevRoleListeners<T> devRoleListeners =
+                (DevRoleListeners<T>) mDevRoleForCapturePresetListeners.get(deviceRole);
+        if (devRoleListeners == null) {
+            return AudioSystem.ERROR;
+        }
+        synchronized (devRoleListeners.mDevRoleListenersLock) {
+            if (devRoleListeners.hasDevRoleListener(listener)) {
+                return AudioSystem.BAD_VALUE;
+            }
+            // lazy initialization of the list of device role listener
+            if (devRoleListeners.mListenerInfos == null) {
+                devRoleListeners.mListenerInfos = new ArrayList<>();
+            }
+            final int oldCbCount = devRoleListeners.mListenerInfos.size();
+            devRoleListeners.mListenerInfos.add(new DevRoleListenerInfo<T>(executor, listener));
+            if (oldCbCount == 0 && devRoleListeners.mListenerInfos.size() > 0) {
+                // register binder for callbacks
+                synchronized (mDevRoleForCapturePresetListenersLock) {
+                    int deviceRoleListenerStatus = mDeviceRoleListenersStatus;
+                    mDeviceRoleListenersStatus |= (1 << deviceRole);
+                    if (deviceRoleListenerStatus != 0) {
+                        // There are already device role changed listeners active.
+                        return AudioSystem.SUCCESS;
+                    }
+                    if (mDevicesRoleForCapturePresetDispatcherStub == null) {
+                        mDevicesRoleForCapturePresetDispatcherStub =
+                                new CapturePresetDevicesRoleDispatcherStub();
+                    }
+                    try {
+                        getService().registerCapturePresetDevicesRoleDispatcher(
+                                mDevicesRoleForCapturePresetDispatcherStub);
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                }
+            }
+        }
+        return AudioSystem.SUCCESS;
+    }
+
+    private <T> int removeOnDevRoleForCapturePresetChangedListener(
+            @NonNull T listener, int deviceRole) {
+        Objects.requireNonNull(listener);
+        DevRoleListeners<T> devRoleListeners =
+                (DevRoleListeners<T>) mDevRoleForCapturePresetListeners.get(deviceRole);
+        if (devRoleListeners == null) {
+            return AudioSystem.ERROR;
+        }
+        synchronized (devRoleListeners.mDevRoleListenersLock) {
+            if (!devRoleListeners.removeDevRoleListener(listener)) {
+                return AudioSystem.BAD_VALUE;
+            }
+            if (devRoleListeners.mListenerInfos.size() == 0) {
+                // unregister binder for callbacks
+                synchronized (mDevRoleForCapturePresetListenersLock) {
+                    mDeviceRoleListenersStatus ^= (1 << deviceRole);
+                    if (mDeviceRoleListenersStatus != 0) {
+                        // There are some other device role changed listeners active.
+                        return AudioSystem.SUCCESS;
+                    }
+                    try {
+                        getService().unregisterCapturePresetDevicesRoleDispatcher(
+                                mDevicesRoleForCapturePresetDispatcherStub);
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                }
+            }
+        }
+        return AudioSystem.SUCCESS;
+    }
+
+    private final Map<Integer, Object> mDevRoleForCapturePresetListeners = new HashMap<>(){{
+            put(AudioSystem.DEVICE_ROLE_PREFERRED,
+                    new DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>());
+        }};
+
+    private class DevRoleListenerInfo<T> {
+        final @NonNull Executor mExecutor;
+        final @NonNull T mListener;
+        DevRoleListenerInfo(Executor executor, T listener) {
+            mExecutor = executor;
+            mListener = listener;
+        }
+    }
+
+    private class DevRoleListeners<T> {
+        private final Object mDevRoleListenersLock = new Object();
+        @GuardedBy("mDevRoleListenersLock")
+        private @Nullable ArrayList<DevRoleListenerInfo<T>> mListenerInfos;
+
+        @GuardedBy("mDevRoleListenersLock")
+        private @Nullable DevRoleListenerInfo<T> getDevRoleListenerInfo(T listener) {
+            if (mListenerInfos == null) {
+                return null;
+            }
+            for (DevRoleListenerInfo<T> listenerInfo : mListenerInfos) {
+                if (listenerInfo.mListener == listener) {
+                    return listenerInfo;
+                }
+            }
+            return null;
+        }
+
+        @GuardedBy("mDevRoleListenersLock")
+        private boolean hasDevRoleListener(T listener) {
+            return getDevRoleListenerInfo(listener) != null;
+        }
+
+        @GuardedBy("mDevRoleListenersLock")
+        private boolean removeDevRoleListener(T listener) {
+            final DevRoleListenerInfo<T> infoToRemove = getDevRoleListenerInfo(listener);
+            if (infoToRemove != null) {
+                mListenerInfos.remove(infoToRemove);
+                return true;
+            }
+            return false;
+        }
+    }
+
+    private final Object mDevRoleForCapturePresetListenersLock = new Object();
+    /**
+     * Record if there is a listener added for device role change. If there is a listener added for
+     * a specified device role change, the bit at position `1 << device_role` is set.
+     */
+    @GuardedBy("mDevRoleForCapturePresetListenersLock")
+    private int mDeviceRoleListenersStatus = 0;
+    @GuardedBy("mDevRoleForCapturePresetListenersLock")
+    private CapturePresetDevicesRoleDispatcherStub mDevicesRoleForCapturePresetDispatcherStub;
+
+    private final class CapturePresetDevicesRoleDispatcherStub
+            extends ICapturePresetDevicesRoleDispatcher.Stub {
+
+        @Override
+        public void dispatchDevicesRoleChanged(
+                int capturePreset, int role, List<AudioDeviceAttributes> devices) {
+            final Object listenersObj = mDevRoleForCapturePresetListeners.get(role);
+            if (listenersObj == null) {
+                return;
+            }
+            switch (role) {
+                case AudioSystem.DEVICE_ROLE_PREFERRED: {
+                    final DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>
+                            listeners =
+                            (DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>)
+                            listenersObj;
+                    final ArrayList<DevRoleListenerInfo<
+                            OnPreferredDevicesForCapturePresetChangedListener>> prefDevListeners;
+                    synchronized (listeners.mDevRoleListenersLock) {
+                        if (listeners.mListenerInfos.isEmpty()) {
+                            return;
+                        }
+                        prefDevListeners = (ArrayList<DevRoleListenerInfo<
+                                OnPreferredDevicesForCapturePresetChangedListener>>)
+                                listeners.mListenerInfos.clone();
+                    }
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        for (DevRoleListenerInfo<
+                                OnPreferredDevicesForCapturePresetChangedListener> info :
+                                prefDevListeners) {
+                            info.mExecutor.execute(() ->
+                                    info.mListener.onPreferredDevicesForCapturePresetChanged(
+                                            capturePreset, devices));
+                        }
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                } break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    //====================================================================
     // Offload query
     /**
      * Returns whether offloaded playback of an audio format is supported on the device.
@@ -5664,6 +6124,29 @@
     }
 
     /**
+     * Returns an {@link AudioDeviceInfo} corresponding to the specified {@link AudioPort} ID.
+     * @param portId The audio port ID to look up for.
+     * @param flags A set of bitflags specifying the criteria to test.
+     * @see #GET_DEVICES_OUTPUTS
+     * @see #GET_DEVICES_INPUTS
+     * @see #GET_DEVICES_ALL
+     * @return An AudioDeviceInfo or null if no device with matching port ID is found.
+     * @hide
+     */
+    public static AudioDeviceInfo getDeviceForPortId(int portId, int flags) {
+        if (portId == 0) {
+            return null;
+        }
+        AudioDeviceInfo[] devices = getDevicesStatic(flags);
+        for (AudioDeviceInfo device : devices) {
+            if (device.getId() == portId) {
+                return device;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Registers an {@link AudioDeviceCallback} object to receive notifications of changes
      * to the set of connected audio devices.
      * @param callback The {@link AudioDeviceCallback} object to receive connect/disconnect
@@ -6206,6 +6689,297 @@
         }
     }
 
+    /**
+     * Selects the audio device that should be used for communication use cases, for instance voice
+     * or video calls. This method can be used by voice or video chat applications to select a
+     * different audio device than the one selected by default by the platform.
+     * <p>The device selection is expressed as an {@link AudioDeviceInfo}, of role sink
+     * ({@link AudioDeviceInfo#isSink()} is <code>true</code>) and of one of the following types:
+     * <ul>
+     *   <li> {@link AudioDeviceInfo#TYPE_BUILTIN_EARPIECE}
+     *   <li> {@link AudioDeviceInfo#TYPE_BUILTIN_SPEAKER}
+     *   <li> {@link AudioDeviceInfo#TYPE_WIRED_HEADSET}
+     *   <li> {@link AudioDeviceInfo#TYPE_BLUETOOTH_SCO}
+     *   <li> {@link AudioDeviceInfo#TYPE_USB_HEADSET}
+     *   <li> {@link AudioDeviceInfo#TYPE_BLE_HEADSET}
+     * </ul>
+     * The selection is active as long as the requesting application lives, until
+     * {@link #clearDeviceForCommunication} is called or until the device is disconnected.
+     * It is therefore important for applications to clear the request when a call ends or the
+     * application is paused.
+     * <p>In case of simultaneous requests by multiple applications the priority is given to the
+     * application currently controlling the audio mode (see {@link #setMode(int)}). This is the
+     * latest application having selected mode {@link #MODE_IN_COMMUNICATION} or mode
+     * {@link #MODE_IN_CALL}. Note that <code>MODE_IN_CALL</code> can only be selected by the main
+     * telephony application with permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE}.
+     * <p> If the requested devices is not currently available, the request will be rejected and
+     * the method will return false.
+     * <p>This API replaces the following deprecated APIs:
+     * <ul>
+     *   <li> {@link #startBluetoothSco()}
+     *   <li> {@link #stopBluetoothSco()}
+     *   <li> {@link #setSpeakerphoneOn(boolean)}
+     * </ul>
+     * <h4>Example</h4>
+     * <p>The example below shows how to enable and disable speakerphone mode.
+     * <pre class="prettyprint">
+     * // Get an AudioManager instance
+     * AudioManager audioManager = Context.getSystemService(AudioManager.class);
+     * try {
+     *     AudioDeviceInfo speakerDevice = null;
+     *     AudioDeviceInfo[] devices = audioManager.getDevices(GET_DEVICES_OUTPUTS);
+     *     for (AudioDeviceInfo device : devices) {
+     *         if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
+     *             speakerDevice = device;
+     *             break;
+     *         }
+     *     }
+     *     if (speakerDevice != null) {
+     *         // Turn speakerphone ON.
+     *         boolean result = audioManager.setDeviceForCommunication(speakerDevice);
+     *         if (!result) {
+     *             // Handle error.
+     *         }
+     *         // Turn speakerphone OFF.
+     *         audioManager.clearDeviceForCommunication();
+     *     }
+     * } catch (IllegalArgumentException e) {
+     *     // Handle exception.
+     * }
+     * </pre>
+     * @param device the requested audio device.
+     * @return <code>true</code> if the request was accepted, <code>false</code> otherwise.
+     * @throws IllegalArgumentException If an invalid device is specified.
+     */
+    public boolean setDeviceForCommunication(@NonNull AudioDeviceInfo device) {
+        Objects.requireNonNull(device);
+        try {
+            if (device.getId() == 0) {
+                throw new IllegalArgumentException("In valid device: " + device);
+            }
+            return getService().setDeviceForCommunication(mICallBack, device.getId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Cancels previous communication device selection made with
+     * {@link #setDeviceForCommunication(AudioDeviceInfo)}.
+     */
+    public void clearDeviceForCommunication() {
+        try {
+            getService().setDeviceForCommunication(mICallBack, 0);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns currently selected audio device for communication.
+     * <p>This API replaces the following deprecated APIs:
+     * <ul>
+     *   <li> {@link #isBluetoothScoOn()}
+     *   <li> {@link #isSpeakerphoneOn()}
+     * </ul>
+     * @return an {@link AudioDeviceInfo} indicating which audio device is
+     * currently selected or communication use cases or null if default selection
+     * is used.
+     */
+    @Nullable
+    public AudioDeviceInfo getDeviceForCommunication() {
+        try {
+            return getDeviceForPortId(
+                    getService().getDeviceForCommunication(), GET_DEVICES_OUTPUTS);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Returns an {@link AudioDeviceInfo} corresponding to a connected device of the type provided.
+     * The type must be a valid output type defined in <code>AudioDeviceInfo</code> class,
+     * for instance {@link AudioDeviceInfo#TYPE_BUILTIN_SPEAKER}.
+     * The method will return null if no device of the provided type is connected.
+     * If more than one device of the provided type is connected, an object corresponding to the
+     * first device encountered in the enumeration list will be returned.
+     * @param deviceType The device device for which an <code>AudioDeviceInfo</code>
+     * object is queried.
+     * @return An AudioDeviceInfo object or null if no device with the requested type is connected.
+     * @throws IllegalArgumentException If an invalid device type is specified.
+     */
+    @TestApi
+    @Nullable
+    public static AudioDeviceInfo getDeviceInfoFromType(
+            @AudioDeviceInfo.AudioDeviceTypeOut int deviceType) {
+        AudioDeviceInfo[] devices = getDevicesStatic(GET_DEVICES_OUTPUTS);
+        for (AudioDeviceInfo device : devices) {
+            if (device.getType() == deviceType) {
+                return device;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Listener registered by client to be notified upon communication audio device change.
+     * See {@link #setDeviceForCommunication(AudioDeviceInfo)}.
+     */
+    public interface OnCommunicationDeviceChangedListener {
+        /**
+         * Callback method called upon communication audio device change.
+         * @param device the audio device selected for communication use cases
+         */
+        void onCommunicationDeviceChanged(@Nullable AudioDeviceInfo device);
+    }
+
+    /**
+     * Adds a listener for being notified of changes to the communication audio device.
+     * See {@link #setDeviceForCommunication(AudioDeviceInfo)}.
+     * @param executor
+     * @param listener
+     */
+    public void addOnCommunicationDeviceChangedListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnCommunicationDeviceChangedListener listener) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(listener);
+        synchronized (mCommDevListenerLock) {
+            if (hasCommDevListener(listener)) {
+                throw new IllegalArgumentException(
+                        "attempt to call addOnCommunicationDeviceChangedListener() "
+                                + "on a previously registered listener");
+            }
+            // lazy initialization of the list of strategy-preferred device listener
+            if (mCommDevListeners == null) {
+                mCommDevListeners = new ArrayList<>();
+            }
+            final int oldCbCount = mCommDevListeners.size();
+            mCommDevListeners.add(new CommDevListenerInfo(listener, executor));
+            if (oldCbCount == 0 && mCommDevListeners.size() > 0) {
+                // register binder for callbacks
+                if (mCommDevDispatcherStub == null) {
+                    mCommDevDispatcherStub = new CommunicationDeviceDispatcherStub();
+                }
+                try {
+                    getService().registerCommunicationDeviceDispatcher(mCommDevDispatcherStub);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes a previously added listener of changes to the communication audio device.
+     * See {@link #setDeviceForCommunication(AudioDeviceInfo)}.
+     * @param listener
+     */
+    public void removeOnCommunicationDeviceChangedListener(
+            @NonNull OnCommunicationDeviceChangedListener listener) {
+        Objects.requireNonNull(listener);
+        synchronized (mCommDevListenerLock) {
+            if (!removeCommDevListener(listener)) {
+                throw new IllegalArgumentException(
+                        "attempt to call removeOnCommunicationDeviceChangedListener() "
+                                + "on an unregistered listener");
+            }
+            if (mCommDevListeners.size() == 0) {
+                // unregister binder for callbacks
+                try {
+                    getService().unregisterCommunicationDeviceDispatcher(
+                            mCommDevDispatcherStub);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                } finally {
+                    mCommDevDispatcherStub = null;
+                    mCommDevListeners = null;
+                }
+            }
+        }
+    }
+
+    private final Object mCommDevListenerLock = new Object();
+    /**
+     * List of listeners for preferred device for strategy and their associated Executor.
+     * List is lazy-initialized on first registration
+     */
+    @GuardedBy("mCommDevListenerLock")
+    private @Nullable ArrayList<CommDevListenerInfo> mCommDevListeners;
+
+    private static class CommDevListenerInfo {
+        final @NonNull OnCommunicationDeviceChangedListener mListener;
+        final @NonNull Executor mExecutor;
+
+        CommDevListenerInfo(OnCommunicationDeviceChangedListener listener, Executor exe) {
+            mListener = listener;
+            mExecutor = exe;
+        }
+    }
+
+    @GuardedBy("mCommDevListenerLock")
+    private CommunicationDeviceDispatcherStub mCommDevDispatcherStub;
+
+    private final class CommunicationDeviceDispatcherStub
+            extends ICommunicationDeviceDispatcher.Stub {
+
+        @Override
+        public void dispatchCommunicationDeviceChanged(int portId) {
+            // make a shallow copy of listeners so callback is not executed under lock
+            final ArrayList<CommDevListenerInfo> commDevListeners;
+            synchronized (mCommDevListenerLock) {
+                if (mCommDevListeners == null || mCommDevListeners.size() == 0) {
+                    return;
+                }
+                commDevListeners = (ArrayList<CommDevListenerInfo>) mCommDevListeners.clone();
+            }
+            AudioDeviceInfo device = getDeviceForPortId(portId, GET_DEVICES_OUTPUTS);
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                for (CommDevListenerInfo info : commDevListeners) {
+                    info.mExecutor.execute(() ->
+                            info.mListener.onCommunicationDeviceChanged(device));
+                }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @GuardedBy("mCommDevListenerLock")
+    private @Nullable CommDevListenerInfo getCommDevListenerInfo(
+            OnCommunicationDeviceChangedListener listener) {
+        if (mCommDevListeners == null) {
+            return null;
+        }
+        for (CommDevListenerInfo info : mCommDevListeners) {
+            if (info.mListener == listener) {
+                return info;
+            }
+        }
+        return null;
+    }
+
+    @GuardedBy("mCommDevListenerLock")
+    private boolean hasCommDevListener(OnCommunicationDeviceChangedListener listener) {
+        return getCommDevListenerInfo(listener) != null;
+    }
+
+    @GuardedBy("mCommDevListenerLock")
+    /**
+     * @return true if the listener was removed from the list
+     */
+    private boolean removeCommDevListener(OnCommunicationDeviceChangedListener listener) {
+        final CommDevListenerInfo infoToRemove = getCommDevListenerInfo(listener);
+        if (infoToRemove != null) {
+            mCommDevListeners.remove(infoToRemove);
+            return true;
+        }
+        return false;
+    }
+
     //---------------------------------------------------------
     // Inner classes
     //--------------------
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index f1eb53d..18c8a72 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -28,11 +28,13 @@
 import android.os.Build;
 import android.telephony.TelephonyManager;
 import android.util.Log;
+import android.util.Pair;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -1371,6 +1373,11 @@
     /** @hide */ public static final int FOR_VIBRATE_RINGING = 7;
     private static final int NUM_FORCE_USE = 8;
 
+    // Device role in audio policy
+    public static final int DEVICE_ROLE_NONE = 0;
+    public static final int DEVICE_ROLE_PREFERRED = 1;
+    public static final int DEVICE_ROLE_DISABLED = 2;
+
     /** @hide */
     public static String forceUseUsageToString(int usage) {
         switch (usage) {
@@ -1691,47 +1698,186 @@
 
     /**
      * @hide
-     * Sets the preferred device to use for a given audio strategy in the audio policy engine
+     * Set device as role for product strategy.
      * @param strategy the id of the strategy to configure
-     * @param device the device type and address to route to when available
+     * @param role the role of the devices
+     * @param devices the list of devices to be set as role for the given strategy
      * @return {@link #SUCCESS} if successfully set
      */
-    public static int setPreferredDeviceForStrategy(
-            int strategy, @NonNull AudioDeviceAttributes device) {
-        return setPreferredDeviceForStrategy(strategy,
-                AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()),
-                device.getAddress());
+    public static int setDevicesRoleForStrategy(
+            int strategy, int role, @NonNull List<AudioDeviceAttributes> devices) {
+        if (devices.isEmpty()) {
+            return BAD_VALUE;
+        }
+        int[] types = new int[devices.size()];
+        String[] addresses = new String[devices.size()];
+        for (int i = 0; i < devices.size(); ++i) {
+            types[i] = devices.get(i).getInternalType();
+            addresses[i] = devices.get(i).getAddress();
+        }
+        return setDevicesRoleForStrategy(strategy, role, types, addresses);
     }
-    /**
-     * @hide
-     * Set device routing per product strategy.
-     * @param strategy the id of the strategy to configure
-     * @param deviceType the native device type, NOT AudioDeviceInfo types
-     * @param deviceAddress the address of the device
-     * @return {@link #SUCCESS} if successfully set
-     */
-    private static native int setPreferredDeviceForStrategy(
-            int strategy, int deviceType, String deviceAddress);
 
     /**
      * @hide
-     * Remove preferred routing for the strategy
+     * Set device as role for product strategy.
      * @param strategy the id of the strategy to configure
+     * @param role the role of the devices
+     * @param types all device types
+     * @param addresses all device addresses
+     * @return {@link #SUCCESS} if successfully set
+     */
+    private static native int setDevicesRoleForStrategy(
+            int strategy, int role, @NonNull int[] types, @NonNull String[] addresses);
+
+    /**
+     * @hide
+     * Remove devices as role for the strategy
+     * @param strategy the id of the strategy to configure
+     * @param role the role of the devices
      * @return {@link #SUCCESS} if successfully removed
      */
-    public static native int removePreferredDeviceForStrategy(int strategy);
+    public static native int removeDevicesRoleForStrategy(int strategy, int role);
 
     /**
      * @hide
-     * Query previously set preferred device for a strategy
+     * Query previously set devices as role for a strategy
      * @param strategy the id of the strategy to query for
-     * @param device an array of size 1 that will contain the preferred device, or null if
-     *               none was set
+     * @param role the role of the devices
+     * @param devices a list that will contain the devices of role
      * @return {@link #SUCCESS} if there is a preferred device and it was successfully retrieved
      *     and written to the array
      */
-    public static native int getPreferredDeviceForStrategy(int strategy,
-                                                           AudioDeviceAttributes[] device);
+    public static native int getDevicesForRoleAndStrategy(
+            int strategy, int role, @NonNull List<AudioDeviceAttributes> devices);
+
+    // use case routing by capture preset
+
+    private static Pair<int[], String[]> populateInputDevicesTypeAndAddress(
+            @NonNull List<AudioDeviceAttributes> devices) {
+        int[] types = new int[devices.size()];
+        String[] addresses = new String[devices.size()];
+        for (int i = 0; i < devices.size(); ++i) {
+            types[i] = devices.get(i).getInternalType();
+            if (types[i] == AudioSystem.DEVICE_NONE) {
+                types[i] = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(
+                        devices.get(i).getType());
+            }
+            addresses[i] = devices.get(i).getAddress();
+        }
+        return new Pair<int[], String[]>(types, addresses);
+    }
+
+    /**
+     * @hide
+     * Set devices as role for capture preset.
+     * @param capturePreset the capture preset to configure
+     * @param role the role of the devices
+     * @param devices the list of devices to be set as role for the given capture preset
+     * @return {@link #SUCCESS} if successfully set
+     */
+    public static int setDevicesRoleForCapturePreset(
+            int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
+        if (devices.isEmpty()) {
+            return BAD_VALUE;
+        }
+        Pair<int[], String[]> typeAddresses = populateInputDevicesTypeAndAddress(devices);
+        return setDevicesRoleForCapturePreset(
+                capturePreset, role, typeAddresses.first, typeAddresses.second);
+    }
+
+    /**
+     * @hide
+     * Set devices as role for capture preset.
+     * @param capturePreset the capture preset to configure
+     * @param role the role of the devices
+     * @param types all device types
+     * @param addresses all device addresses
+     * @return {@link #SUCCESS} if successfully set
+     */
+    private static native int setDevicesRoleForCapturePreset(
+            int capturePreset, int role, @NonNull int[] types, @NonNull String[] addresses);
+
+    /**
+     * @hide
+     * Add devices as role for capture preset.
+     * @param capturePreset the capture preset to configure
+     * @param role the role of the devices
+     * @param devices the list of devices to be added as role for the given capture preset
+     * @return {@link #SUCCESS} if successfully add
+     */
+    public static int addDevicesRoleForCapturePreset(
+            int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
+        if (devices.isEmpty()) {
+            return BAD_VALUE;
+        }
+        Pair<int[], String[]> typeAddresses = populateInputDevicesTypeAndAddress(devices);
+        return addDevicesRoleForCapturePreset(
+                capturePreset, role, typeAddresses.first, typeAddresses.second);
+    }
+
+    /**
+     * @hide
+     * Add devices as role for capture preset.
+     * @param capturePreset the capture preset to configure
+     * @param role the role of the devices
+     * @param types all device types
+     * @param addresses all device addresses
+     * @return {@link #SUCCESS} if successfully set
+     */
+    private static native int addDevicesRoleForCapturePreset(
+            int capturePreset, int role, @NonNull int[] types, @NonNull String[] addresses);
+
+    /**
+     * @hide
+     * Remove devices as role for the capture preset
+     * @param capturePreset the capture preset to configure
+     * @param role the role of the devices
+     * @param devices the devices to be removed
+     * @return {@link #SUCCESS} if successfully removed
+     */
+    public static int removeDevicesRoleForCapturePreset(
+            int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
+        if (devices.isEmpty()) {
+            return BAD_VALUE;
+        }
+        Pair<int[], String[]> typeAddresses = populateInputDevicesTypeAndAddress(devices);
+        return removeDevicesRoleForCapturePreset(
+                capturePreset, role, typeAddresses.first, typeAddresses.second);
+    }
+
+    /**
+     * @hide
+     * Remove devices as role for capture preset.
+     * @param capturePreset the capture preset to configure
+     * @param role the role of the devices
+     * @param types all device types
+     * @param addresses all device addresses
+     * @return {@link #SUCCESS} if successfully set
+     */
+    private static native int removeDevicesRoleForCapturePreset(
+            int capturePreset, int role, @NonNull int[] types, @NonNull String[] addresses);
+
+    /**
+     * @hide
+     * Remove all devices as role for the capture preset
+     * @param capturePreset the capture preset to configure
+     * @param role the role of the devices
+     * @return {@link #SUCCESS} if successfully removed
+     */
+    public static native int clearDevicesRoleForCapturePreset(int capturePreset, int role);
+
+    /**
+     * @hide
+     * Query previously set devices as role for a capture preset
+     * @param capturePreset the capture preset to query for
+     * @param role the role of the devices
+     * @param devices a list that will contain the devices of role
+     * @return {@link #SUCCESS} if there is a preferred device and it was successfully retrieved
+     *     and written to the array
+     */
+    public static native int getDevicesForRoleAndCapturePreset(
+            int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices);
 
     // Items shared with audio service
 
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 78806eb..ebaa316 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -26,10 +26,12 @@
 import android.media.IAudioFocusDispatcher;
 import android.media.IAudioRoutesObserver;
 import android.media.IAudioServerStateDispatcher;
+import android.media.ICapturePresetDevicesRoleDispatcher;
+import android.media.ICommunicationDeviceDispatcher;
 import android.media.IPlaybackConfigDispatcher;
 import android.media.IRecordingConfigDispatcher;
 import android.media.IRingtonePlayer;
-import android.media.IStrategyPreferredDeviceDispatcher;
+import android.media.IStrategyPreferredDevicesDispatcher;
 import android.media.IVolumeController;
 import android.media.IVolumeController;
 import android.media.PlayerBase;
@@ -279,11 +281,11 @@
 
     boolean isCallScreeningModeSupported();
 
-    int setPreferredDeviceForStrategy(in int strategy, in AudioDeviceAttributes device);
+    int setPreferredDevicesForStrategy(in int strategy, in List<AudioDeviceAttributes> device);
 
-    int removePreferredDeviceForStrategy(in int strategy);
+    int removePreferredDevicesForStrategy(in int strategy);
 
-    AudioDeviceAttributes getPreferredDeviceForStrategy(in int strategy);
+    List<AudioDeviceAttributes> getPreferredDevicesForStrategy(in int strategy);
 
     List<AudioDeviceAttributes> getDevicesForAttributes(in AudioAttributes attributes);
 
@@ -291,10 +293,10 @@
 
     int getAllowedCapturePolicy();
 
-    void registerStrategyPreferredDeviceDispatcher(IStrategyPreferredDeviceDispatcher dispatcher);
+    void registerStrategyPreferredDevicesDispatcher(IStrategyPreferredDevicesDispatcher dispatcher);
 
-    oneway void unregisterStrategyPreferredDeviceDispatcher(
-            IStrategyPreferredDeviceDispatcher dispatcher);
+    oneway void unregisterStrategyPreferredDevicesDispatcher(
+            IStrategyPreferredDevicesDispatcher dispatcher);
 
     oneway void setRttEnabled(in boolean rttEnabled);
 
@@ -307,4 +309,25 @@
     // code via IAudioManager.h need to be added to the top section.
 
     oneway void setMultiAudioFocusEnabled(in boolean enabled);
+
+    int setPreferredDevicesForCapturePreset(
+            in int capturePreset, in List<AudioDeviceAttributes> devices);
+
+    int clearPreferredDevicesForCapturePreset(in int capturePreset);
+
+    List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(in int capturePreset);
+
+    void registerCapturePresetDevicesRoleDispatcher(ICapturePresetDevicesRoleDispatcher dispatcher);
+
+    oneway void unregisterCapturePresetDevicesRoleDispatcher(
+            ICapturePresetDevicesRoleDispatcher dispatcher);
+
+    boolean setDeviceForCommunication(IBinder cb, int portId);
+
+    int getDeviceForCommunication();
+
+    void registerCommunicationDeviceDispatcher(ICommunicationDeviceDispatcher dispatcher);
+
+    oneway void unregisterCommunicationDeviceDispatcher(
+            ICommunicationDeviceDispatcher dispatcher);
 }
diff --git a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl b/media/java/android/media/ICapturePresetDevicesRoleDispatcher.aidl
similarity index 74%
copy from media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
copy to media/java/android/media/ICapturePresetDevicesRoleDispatcher.aidl
index b1f99e6..5e03e63 100644
--- a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
+++ b/media/java/android/media/ICapturePresetDevicesRoleDispatcher.aidl
@@ -19,12 +19,13 @@
 import android.media.AudioDeviceAttributes;
 
 /**
- * AIDL for AudioService to signal audio strategy-preferred device updates.
+ * AIDL for AudioService to signal devices role for capture preset updates.
  *
  * {@hide}
  */
-oneway interface IStrategyPreferredDeviceDispatcher {
+oneway interface ICapturePresetDevicesRoleDispatcher {
 
-    void dispatchPrefDeviceChanged(int strategyId, in AudioDeviceAttributes device);
+    void dispatchDevicesRoleChanged(
+            int capturePreset, int role, in List<AudioDeviceAttributes> devices);
 
 }
diff --git a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl b/media/java/android/media/ICommunicationDeviceDispatcher.aidl
similarity index 72%
copy from media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
copy to media/java/android/media/ICommunicationDeviceDispatcher.aidl
index b1f99e6..429f934 100644
--- a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
+++ b/media/java/android/media/ICommunicationDeviceDispatcher.aidl
@@ -16,15 +16,13 @@
 
 package android.media;
 
-import android.media.AudioDeviceAttributes;
-
 /**
- * AIDL for AudioService to signal audio strategy-preferred device updates.
+ * AIDL for AudioService to signal audio communication device updates.
  *
  * {@hide}
  */
-oneway interface IStrategyPreferredDeviceDispatcher {
+oneway interface ICommunicationDeviceDispatcher {
 
-    void dispatchPrefDeviceChanged(int strategyId, in AudioDeviceAttributes device);
+    void dispatchCommunicationDeviceChanged(int portId);
 
 }
diff --git a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl b/media/java/android/media/IStrategyPreferredDevicesDispatcher.aidl
similarity index 82%
rename from media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
rename to media/java/android/media/IStrategyPreferredDevicesDispatcher.aidl
index b1f99e6..db674c3 100644
--- a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
+++ b/media/java/android/media/IStrategyPreferredDevicesDispatcher.aidl
@@ -19,12 +19,12 @@
 import android.media.AudioDeviceAttributes;
 
 /**
- * AIDL for AudioService to signal audio strategy-preferred device updates.
+ * AIDL for AudioService to signal audio strategy-preferred devices updates.
  *
  * {@hide}
  */
-oneway interface IStrategyPreferredDeviceDispatcher {
+oneway interface IStrategyPreferredDevicesDispatcher {
 
-    void dispatchPrefDeviceChanged(int strategyId, in AudioDeviceAttributes device);
+    void dispatchPrefDevicesChanged(int strategyId, in List<AudioDeviceAttributes> devices);
 
 }
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 73ef315..c61a2eb 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -404,6 +404,32 @@
         }
     }
 
+    /**
+     * @hide
+     * @param source An audio source to test
+     * @return true if the source is a valid one
+     */
+    public static boolean isValidAudioSource(int source) {
+        switch(source) {
+            case AudioSource.MIC:
+            case AudioSource.VOICE_UPLINK:
+            case AudioSource.VOICE_DOWNLINK:
+            case AudioSource.VOICE_CALL:
+            case AudioSource.CAMCORDER:
+            case AudioSource.VOICE_RECOGNITION:
+            case AudioSource.VOICE_COMMUNICATION:
+            case AudioSource.REMOTE_SUBMIX:
+            case AudioSource.UNPROCESSED:
+            case AudioSource.VOICE_PERFORMANCE:
+            case AudioSource.ECHO_REFERENCE:
+            case AudioSource.RADIO_TUNER:
+            case AudioSource.HOTWORD:
+                return true;
+            default:
+                return false;
+        }
+    }
+
     /** @hide */
     public static final String toLogFriendlyAudioSource(int source) {
         switch(source) {
diff --git a/media/java/android/media/OWNERS b/media/java/android/media/OWNERS
index cbc9ab7..cf06fad 100644
--- a/media/java/android/media/OWNERS
+++ b/media/java/android/media/OWNERS
@@ -6,3 +6,4 @@
 olly@google.com
 andrewlewis@google.com
 sungsoo@google.com
+jmtrivi@google.com
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index d55e9d0..0d53ab1 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -333,7 +333,7 @@
     if (deviceType != AUDIO_DEVICE_NONE) {
         device.mType = (audio_devices_t)deviceType;
         ScopedUtfChars address(env, deviceAddress);
-        device.mAddress = address.c_str();
+        device.setAddress(address.c_str());
     }
 
     // create the native AudioEffect object
diff --git a/native/android/OWNERS b/native/android/OWNERS
index 266764a..ac5a895 100644
--- a/native/android/OWNERS
+++ b/native/android/OWNERS
@@ -1,4 +1,3 @@
-set noparent
-
+per-file libandroid_net.map.txt, net.c = set noparent
 per-file libandroid_net.map.txt, net.c = codewiz@google.com, jchalard@google.com, junyulai@google.com
 per-file libandroid_net.map.txt, net.c = lorenzo@google.com, reminv@google.com, satk@google.com
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 2e1fbb7..01ef69b 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1803,7 +1803,8 @@
 
     private INetworkManagementEventObserver mDataActivityObserver = new BaseNetworkObserver() {
         @Override
-        public void interfaceClassDataActivityChanged(String label, boolean active, long tsNanos) {
+        public void interfaceClassDataActivityChanged(String label, boolean active, long tsNanos,
+                int uid) {
             int deviceType = Integer.parseInt(label);
             sendDataActivityBroadcast(deviceType, active, tsNanos);
         }
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 636da6f..d30adf1 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -396,22 +396,12 @@
      * Notify our observers of a change in the data activity state of the interface
      */
     private void notifyInterfaceClassActivity(int type, boolean isActive, long tsNanos,
-            int uid, boolean fromRadio) {
+            int uid) {
         final boolean isMobile = ConnectivityManager.isNetworkTypeMobile(type);
         int powerState = isActive
                 ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
                 : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
         if (isMobile) {
-            if (!fromRadio) {
-                if (mMobileActivityFromRadio) {
-                    // If this call is not coming from a report from the radio itself, but we
-                    // have previously received reports from the radio, then we will take the
-                    // power state to just be whatever the radio last reported.
-                    powerState = mLastPowerStateFromRadio;
-                }
-            } else {
-                mMobileActivityFromRadio = true;
-            }
             if (mLastPowerStateFromRadio != powerState) {
                 mLastPowerStateFromRadio = powerState;
                 try {
@@ -431,15 +421,9 @@
             }
         }
 
-        if (!isMobile || fromRadio || !mMobileActivityFromRadio) {
-            // Report the change in data activity.  We don't do this if this is a change
-            // on the mobile network, that is not coming from the radio itself, and we
-            // have previously seen change reports from the radio.  In that case only
-            // the radio is the authority for the current state.
-            final boolean active = isActive;
-            invokeForAllObservers(o -> o.interfaceClassDataActivityChanged(
-                    Integer.toString(type), active, tsNanos));
-        }
+        final boolean active = isActive;
+        invokeForAllObservers(o -> o.interfaceClassDataActivityChanged(
+                Integer.toString(type), active, tsNanos, uid));
 
         boolean report = false;
         synchronized (mIdleTimerLock) {
@@ -671,7 +655,7 @@
                 timestampNanos = timestamp;
             }
             mDaemonHandler.post(() ->
-                    notifyInterfaceClassActivity(label, isActive, timestampNanos, uid, false));
+                    notifyInterfaceClassActivity(label, isActive, timestampNanos, uid));
         }
 
         @Override
@@ -1157,7 +1141,7 @@
                 mNetworkActive = false;
             }
             mDaemonHandler.post(() -> notifyInterfaceClassActivity(type, true,
-                    SystemClock.elapsedRealtimeNanos(), -1, false));
+                    SystemClock.elapsedRealtimeNanos(), -1));
         }
     }
 
@@ -1181,7 +1165,7 @@
             }
             mActiveIdleTimers.remove(iface);
             mDaemonHandler.post(() -> notifyInterfaceClassActivity(params.type, false,
-                    SystemClock.elapsedRealtimeNanos(), -1, false));
+                    SystemClock.elapsedRealtimeNanos(), -1));
         }
     }
 
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 5e85409..74e3851 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -57,12 +57,12 @@
  *
  * <pre>The internal structure of the VCN Management subsystem is as follows:
  *
- * +------------------------+ 1:1                                 +--------------------------------+
- * |  VcnManagementService  | ------------ Creates -------------> |  TelephonySubscriptionManager  |
- * |                        |                                     |                                |
- * | Manages configs and    |                                     | Tracks subscriptions, carrier  |
- * | VcnInstance lifecycles | <--- Notifies of subscription & --- | privilege changes, caches maps |
- * +------------------------+      carrier privilege changes      +--------------------------------+
+ * +-------------------------+ 1:1                                +--------------------------------+
+ * |  VcnManagementService   | ------------ Creates ------------> |  TelephonySubscriptionManager  |
+ * |                         |                                    |                                |
+ * |   Manages configs and   |                                    | Tracks subscriptions, carrier  |
+ * | Vcn instance lifecycles | <--- Notifies of subscription & -- | privilege changes, caches maps |
+ * +-------------------------+      carrier privilege changes     +--------------------------------+
  *      | 1:N          ^
  *      |              |
  *      |              +-------------------------------+
@@ -74,19 +74,19 @@
  *                      |                      mode state changes
  *                      v                              |
  * +-----------------------------------------------------------------------+
- * |                              VcnInstance                              |
+ * |                                  Vcn                                  |
  * |                                                                       |
- * |   Manages tunnel lifecycles based on fulfillable NetworkRequest(s)    |
- * |                        and overall safe-mode                          |
+ * |       Manages GatewayConnection lifecycles based on fulfillable       |
+ * |                NetworkRequest(s) and overall safe-mode                |
  * +-----------------------------------------------------------------------+
  *                      | 1:N                          ^
  *              Creates to fulfill                     |
- *           NetworkRequest(s), tears        Notifies of VcnTunnel
+ *           NetworkRequest(s), tears   Notifies of VcnGatewayConnection
  *          down when no longer needed   teardown (e.g. Network reaped)
  *                      |                 and safe-mode timer changes
  *                      v                              |
  * +-----------------------------------------------------------------------+
- * |                               VcnTunnel                               |
+ * |                          VcnGatewayConnection                         |
  * |                                                                       |
  * |       Manages a single (IKEv2) tunnel session and NetworkAgent,       |
  * |  handles mobility events, (IPsec) Tunnel setup and safe-mode timers   |
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 074d3fe..26f5c4c 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -16,6 +16,7 @@
 package com.android.server.audio;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
@@ -25,19 +26,26 @@
 import android.content.Context;
 import android.content.Intent;
 import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
 import android.media.AudioRoutesInfo;
 import android.media.AudioSystem;
 import android.media.IAudioRoutesObserver;
-import android.media.IStrategyPreferredDeviceDispatcher;
+import android.media.ICapturePresetDevicesRoleDispatcher;
+import android.media.ICommunicationDeviceDispatcher;
+import android.media.IStrategyPreferredDevicesDispatcher;
 import android.media.MediaMetrics;
+import android.media.audiopolicy.AudioProductStrategy;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.PrintWriterPrinter;
@@ -45,8 +53,10 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -70,11 +80,8 @@
     private final @NonNull Context mContext;
 
     /** Forced device usage for communications sent to AudioSystem */
-    private int mForcedUseForComm;
-    /**
-     * Externally reported force device usage state returned by getters: always consistent
-     * with requests by setters */
-    private int mForcedUseForCommExt;
+    private AudioDeviceAttributes mPreferredDeviceforComm;
+    private int mCommunicationStrategyId = -1;
 
     // Manages all connected devices, only ever accessed on the message loop
     private final AudioDeviceInventory mDeviceInventory;
@@ -130,11 +137,23 @@
         init();
     }
 
+    private void initCommunicationStrategyId() {
+        List<AudioProductStrategy> strategies = AudioProductStrategy.getAudioProductStrategies();
+        for (AudioProductStrategy strategy : strategies) {
+            if (strategy.getAudioAttributesForLegacyStreamType(AudioSystem.STREAM_VOICE_CALL)
+                    != null) {
+                mCommunicationStrategyId = strategy.getId();
+                return;
+            }
+        }
+        mCommunicationStrategyId = -1;
+    }
+
     private void init() {
         setupMessaging(mContext);
 
-        mForcedUseForComm = AudioSystem.FORCE_NONE;
-        mForcedUseForCommExt = mForcedUseForComm;
+        mPreferredDeviceforComm = null;
+        initCommunicationStrategyId();
     }
 
     /*package*/ Context getContext() {
@@ -157,15 +176,6 @@
     }
 
     /*package*/ void onAudioServerDied() {
-        // Restore forced usage for communications and record
-        synchronized (mDeviceStateLock) {
-            AudioSystem.setParameters(
-                    "BT_SCO=" + (mForcedUseForComm == AudioSystem.FORCE_BT_SCO ? "on" : "off"));
-            onSetForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm,
-                          false /*fromA2dp*/, "onAudioServerDied");
-            onSetForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm,
-                          false /*fromA2dp*/, "onAudioServerDied");
-        }
         // restore devices
         sendMsgNoDelay(MSG_RESTORE_DEVICES, SENDMSG_REPLACE);
     }
@@ -217,85 +227,210 @@
      * Turns speakerphone on/off
      * @param on
      * @param eventSource for logging purposes
-     * @return true if speakerphone state changed
      */
-    /*package*/ boolean setSpeakerphoneOn(IBinder cb, int pid, boolean on, String eventSource) {
-        synchronized (mDeviceStateLock) {
-            if (!addSpeakerphoneClient(cb, pid, on)) {
-                return false;
+    /*package*/ void setSpeakerphoneOn(IBinder cb, int pid, boolean on, String eventSource) {
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "setSpeakerphoneOn, on: " + on + " pid: " + pid);
+        }
+
+        synchronized (mSetModeLock) {
+            synchronized (mDeviceStateLock) {
+                AudioDeviceAttributes device = null;
+                if (on) {
+                    device = new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, "");
+                } else {
+                    CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
+                    if (client == null || !client.requestsSpeakerphone()) {
+                        return;
+                    }
+                }
+                setCommunicationRouteForClient(
+                        cb, pid, device, BtHelper.SCO_MODE_UNDEFINED, eventSource);
             }
-            if (on) {
-                // Cancel BT SCO ON request by this same client: speakerphone and BT SCO routes
-                // are mutually exclusive.
-                // See symmetrical operation for startBluetoothScoForClient_Sync().
-                mBtHelper.stopBluetoothScoForPid(pid);
-            }
-            final boolean wasOn = isSpeakerphoneOn();
-            updateSpeakerphoneOn(eventSource);
-            return (wasOn != isSpeakerphoneOn());
         }
     }
 
     /**
-     * Turns speakerphone off for a given pid and update speakerphone state.
-     * @param pid
+     * Select device for use for communication use cases.
+     * @param cb Client binder for death detection
+     * @param pid Client pid
+     * @param device Device selected or null to unselect.
+     * @param eventSource for logging purposes
      */
+    /*package*/ boolean setDeviceForCommunication(
+            IBinder cb, int pid, AudioDeviceInfo device, String eventSource) {
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "setDeviceForCommunication, device: " + device + ", pid: " + pid);
+        }
+
+        synchronized (mSetModeLock) {
+            synchronized (mDeviceStateLock) {
+                AudioDeviceAttributes deviceAttr = null;
+                if (device != null) {
+                    deviceAttr = new AudioDeviceAttributes(device);
+                } else {
+                    CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
+                    if (client == null) {
+                        return false;
+                    }
+                }
+                setCommunicationRouteForClient(
+                        cb, pid, deviceAttr, BtHelper.SCO_MODE_UNDEFINED, eventSource);
+            }
+        }
+        return true;
+    }
+
     @GuardedBy("mDeviceStateLock")
-    private void setSpeakerphoneOffForPid(int pid) {
-        SpeakerphoneClient client = getSpeakerphoneClientForPid(pid);
+    /*package*/ void setCommunicationRouteForClient(
+                            IBinder cb, int pid, AudioDeviceAttributes device,
+                            int scoAudioMode, String eventSource) {
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "setCommunicationRouteForClient: device: " + device);
+        }
+        AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
+                                        "setCommunicationRouteForClient for pid: " + pid
+                                        + " device: " + device
+                                        + " from API: " + eventSource)).printLog(TAG));
+
+        final boolean wasBtScoRequested = isBluetoothScoRequested();
+        final boolean wasSpeakerphoneRequested = isSpeakerphoneRequested();
+        CommunicationRouteClient client;
+
+
+        // Save previous client route in case of failure to start BT SCO audio
+        AudioDeviceAttributes prevClientDevice = null;
+        client = getCommunicationRouteClientForPid(pid);
+        if (client != null) {
+            prevClientDevice = client.getDevice();
+        }
+
+        if (device != null) {
+            client = addCommunicationRouteClient(cb, pid, device);
+            if (client == null) {
+                Log.w(TAG, "setCommunicationRouteForClient: could not add client for pid: "
+                        + pid + " and device: " + device);
+            }
+        } else {
+            client = removeCommunicationRouteClient(cb, true);
+        }
         if (client == null) {
             return;
         }
-        client.unregisterDeathRecipient();
-        mSpeakerphoneClients.remove(client);
-        final String eventSource = new StringBuilder("setSpeakerphoneOffForPid(")
-                .append(pid).append(")").toString();
-        updateSpeakerphoneOn(eventSource);
-    }
 
-    @GuardedBy("mDeviceStateLock")
-    private void updateSpeakerphoneOn(String eventSource) {
-        if (isSpeakerphoneOnRequested()) {
-            if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
-                setForceUse_Async(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource);
+        boolean isBtScoRequested = isBluetoothScoRequested();
+        if (isBtScoRequested && !wasBtScoRequested) {
+            if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) {
+                Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for pid: "
+                        + pid);
+                // clean up or restore previous client selection
+                if (prevClientDevice != null) {
+                    addCommunicationRouteClient(cb, pid, prevClientDevice);
+                } else {
+                    removeCommunicationRouteClient(cb, true);
+                }
+                postBroadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
             }
-            mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
-        } else if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
-            if (mBtHelper.isBluetoothScoOn()) {
-                mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
-                setForceUse_Async(
-                        AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO, eventSource);
-            } else {
-                mForcedUseForComm = AudioSystem.FORCE_NONE;
+        } else if (!isBtScoRequested && wasBtScoRequested) {
+            mBtHelper.stopBluetoothSco(eventSource);
+        }
+
+        if (wasSpeakerphoneRequested != isSpeakerphoneRequested()) {
+            try {
+                mContext.sendBroadcastAsUser(
+                        new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
+                                .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
+            } catch (Exception e) {
+                Log.w(TAG, "failed to broadcast ACTION_SPEAKERPHONE_STATE_CHANGED: " + e);
             }
         }
-        mForcedUseForCommExt = mForcedUseForComm;
-        setForceUse_Async(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
+
+        sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource);
     }
 
     /**
-     * Returns if speakerphone is requested ON or OFF.
-     * If the current audio mode owner is in the speakerphone client list, use this preference.
+     * Returns the device currently requested for communication use case.
+     * If the current audio mode owner is in the communication route client list,
+     * use this preference.
      * Otherwise use first client's preference (first client corresponds to latest request).
-     * Speakerphone is requested OFF if no client is in the list.
-     * @return true if speakerphone is requested ON, false otherwise
+     * null is returned if no client is in the list.
+     * @return AudioDeviceAttributes the requested device for communication.
      */
+
     @GuardedBy("mDeviceStateLock")
-    private boolean isSpeakerphoneOnRequested() {
-        if (mSpeakerphoneClients.isEmpty()) {
-            return false;
-        }
-        for (SpeakerphoneClient cl : mSpeakerphoneClients) {
+    private AudioDeviceAttributes requestedCommunicationDevice() {
+        AudioDeviceAttributes device = null;
+        for (CommunicationRouteClient cl : mCommunicationRouteClients) {
             if (cl.getPid() == mModeOwnerPid) {
-                return cl.isOn();
+                device = cl.getDevice();
             }
         }
-        return mSpeakerphoneClients.get(0).isOn();
+        if (!mCommunicationRouteClients.isEmpty() && mModeOwnerPid == 0) {
+            device = mCommunicationRouteClients.get(0).getDevice();
+        }
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "requestedCommunicationDevice, device: "
+                    + device + " mode owner pid: " + mModeOwnerPid);
+        }
+        return device;
     }
 
-    /*package*/ boolean isSpeakerphoneOn() {
+    /**
+     * Returns the device currently requested for communication use case.
+     * @return AudioDeviceInfo the requested device for communication.
+     */
+    AudioDeviceInfo getDeviceForCommunication() {
         synchronized (mDeviceStateLock) {
-            return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER);
+            AudioDeviceAttributes device = requestedCommunicationDevice();
+            if (device == null) {
+                return null;
+            }
+            return AudioManager.getDeviceInfoFromType(device.getType());
+        }
+    }
+
+    /**
+     * Helper method on top of requestedCommunicationDevice() indicating if
+     * speakerphone ON is currently requested or not.
+     * @return true if speakerphone ON requested, false otherwise.
+     */
+
+    private boolean isSpeakerphoneRequested() {
+        synchronized (mDeviceStateLock) {
+            AudioDeviceAttributes device = requestedCommunicationDevice();
+            return device != null
+                    && device.getType()
+                        == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
+        }
+    }
+
+    /**
+     * Indicates if active route selection for communication is speakerphone.
+     * @return true if speakerphone is active, false otherwise.
+     */
+    /*package*/ boolean isSpeakerphoneOn() {
+        AudioDeviceAttributes device = getPreferredDeviceForComm();
+        if (device == null) {
+            return false;
+        }
+        return device.getInternalType() == AudioSystem.DEVICE_OUT_SPEAKER;
+    }
+
+    /**
+     * Helper method on top of requestedCommunicationDevice() indicating if
+     * Bluetooth SCO ON is currently requested or not.
+     * @return true if Bluetooth SCO ON is requested, false otherwise.
+     */
+    /*package*/ boolean isBluetoothScoRequested() {
+        synchronized (mDeviceStateLock) {
+            AudioDeviceAttributes device = requestedCommunicationDevice();
+            return device != null
+                    && device.getType()
+                        == AudioDeviceInfo.TYPE_BLUETOOTH_SCO;
         }
     }
 
@@ -346,7 +481,6 @@
         }
     }
 
-
     /*package*/ void postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
             @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
             int profile, boolean suppressNoisyIntent, int a2dpVolume) {
@@ -429,42 +563,32 @@
         sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
     }
 
-    // never called by system components
-    /*package*/ void setBluetoothScoOnByApp(boolean on) {
-        synchronized (mDeviceStateLock) {
-            mForcedUseForCommExt = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
-        }
-    }
 
-    /*package*/ boolean isBluetoothScoOnForApp() {
-        synchronized (mDeviceStateLock) {
-            return mForcedUseForCommExt == AudioSystem.FORCE_BT_SCO;
-        }
-    }
+    /**
+     * Current Bluetooth SCO audio active state indicated by BtHelper via setBluetoothScoOn().
+     */
+    private boolean mBluetoothScoOn;
 
     /*package*/ void setBluetoothScoOn(boolean on, String eventSource) {
-        //Log.i(TAG, "setBluetoothScoOn: " + on + " " + eventSource);
-        synchronized (mDeviceStateLock) {
-            if (on) {
-                // do not accept SCO ON if SCO audio is not connected
-                if (!mBtHelper.isBluetoothScoOn()) {
-                    mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
-                    return;
-                }
-                mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
-            } else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
-                mForcedUseForComm = isSpeakerphoneOnRequested()
-                        ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE;
-            }
-            mForcedUseForCommExt = mForcedUseForComm;
-            AudioSystem.setParameters("BT_SCO=" + (on ? "on" : "off"));
-            sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
-                    AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
-            sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
-                    AudioSystem.FOR_RECORD, mForcedUseForComm, eventSource);
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "setBluetoothScoOn: " + on + " " + eventSource);
         }
-        // Un-mute ringtone stream volume
-        mAudioService.postUpdateRingerModeServiceInt();
+        synchronized (mDeviceStateLock) {
+            mBluetoothScoOn = on;
+            sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource);
+        }
+    }
+
+    /**
+     * Indicates if active route selection for communication is Bluetooth SCO.
+     * @return true if Bluetooth SCO is active , false otherwise.
+     */
+    /*package*/ boolean isBluetoothScoOn() {
+        AudioDeviceAttributes device = getPreferredDeviceForComm();
+        if (device == null) {
+            return false;
+        }
+        return AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(device.getInternalType());
     }
 
     /*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
@@ -507,42 +631,125 @@
         sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
     }
 
-    @GuardedBy("mSetModeLock")
-    /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode,
+    /*package*/ void startBluetoothScoForClient(IBinder cb, int pid, int scoAudioMode,
                 @NonNull String eventSource) {
-        synchronized (mDeviceStateLock) {
-            // Cancel speakerphone ON request by this same client: speakerphone and BT SCO routes
-            // are mutually exclusive.
-            // See symmetrical operation for setSpeakerphoneOn(true).
-            setSpeakerphoneOffForPid(Binder.getCallingPid());
-            mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource);
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "startBluetoothScoForClient_Sync, pid: " + pid);
+        }
+
+        synchronized (mSetModeLock) {
+            synchronized (mDeviceStateLock) {
+                AudioDeviceAttributes device =
+                        new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, "");
+                setCommunicationRouteForClient(cb, pid, device, scoAudioMode, eventSource);
+            }
         }
     }
 
-    @GuardedBy("mSetModeLock")
-    /*package*/ void stopBluetoothScoForClient_Sync(IBinder cb, @NonNull String eventSource) {
-        synchronized (mDeviceStateLock) {
-            mBtHelper.stopBluetoothScoForClient(cb, eventSource);
+    /*package*/ void stopBluetoothScoForClient(
+                        IBinder cb, int pid, @NonNull String eventSource) {
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "stopBluetoothScoForClient_Sync, pid: " + pid);
+        }
+
+        synchronized (mSetModeLock) {
+            synchronized (mDeviceStateLock) {
+                CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
+                if (client == null || !client.requestsBluetoothSco()) {
+                    return;
+                }
+                setCommunicationRouteForClient(
+                        cb, pid, null, BtHelper.SCO_MODE_UNDEFINED, eventSource);
+            }
         }
     }
 
-    /*package*/ int setPreferredDeviceForStrategySync(int strategy,
-                                                      @NonNull AudioDeviceAttributes device) {
-        return mDeviceInventory.setPreferredDeviceForStrategySync(strategy, device);
+    /*package*/ int setPreferredDevicesForStrategySync(int strategy,
+            @NonNull List<AudioDeviceAttributes> devices) {
+        return mDeviceInventory.setPreferredDevicesForStrategySync(strategy, devices);
     }
 
-    /*package*/ int removePreferredDeviceForStrategySync(int strategy) {
-        return mDeviceInventory.removePreferredDeviceForStrategySync(strategy);
+    /*package*/ void postSetPreferredDevicesForStrategy(int strategy,
+            @NonNull List<AudioDeviceAttributes> devices) {
+        sendILMsgNoDelay(MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY, SENDMSG_REPLACE, strategy, devices);
     }
 
-    /*package*/ void registerStrategyPreferredDeviceDispatcher(
-            @NonNull IStrategyPreferredDeviceDispatcher dispatcher) {
-        mDeviceInventory.registerStrategyPreferredDeviceDispatcher(dispatcher);
+    /*package*/ int removePreferredDevicesForStrategySync(int strategy) {
+        return mDeviceInventory.removePreferredDevicesForStrategySync(strategy);
     }
 
-    /*package*/ void unregisterStrategyPreferredDeviceDispatcher(
-            @NonNull IStrategyPreferredDeviceDispatcher dispatcher) {
-        mDeviceInventory.unregisterStrategyPreferredDeviceDispatcher(dispatcher);
+    /*package*/ void postRemovePreferredDevicesForStrategy(int strategy) {
+        sendIMsgNoDelay(MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_REPLACE, strategy);
+    }
+
+    /*package*/ void registerStrategyPreferredDevicesDispatcher(
+            @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
+        mDeviceInventory.registerStrategyPreferredDevicesDispatcher(dispatcher);
+    }
+
+    /*package*/ void unregisterStrategyPreferredDevicesDispatcher(
+            @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
+        mDeviceInventory.unregisterStrategyPreferredDevicesDispatcher(dispatcher);
+    }
+
+    /*package*/ int setPreferredDevicesForCapturePresetSync(int capturePreset,
+            @NonNull List<AudioDeviceAttributes> devices) {
+        return mDeviceInventory.setPreferredDevicesForCapturePresetSync(capturePreset, devices);
+    }
+
+    /*package*/ int clearPreferredDevicesForCapturePresetSync(int capturePreset) {
+        return mDeviceInventory.clearPreferredDevicesForCapturePresetSync(capturePreset);
+    }
+
+    /*package*/ void registerCapturePresetDevicesRoleDispatcher(
+            @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
+        mDeviceInventory.registerCapturePresetDevicesRoleDispatcher(dispatcher);
+    }
+
+    /*package*/ void unregisterCapturePresetDevicesRoleDispatcher(
+            @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
+        mDeviceInventory.unregisterCapturePresetDevicesRoleDispatcher(dispatcher);
+    }
+
+    /*package*/ void registerCommunicationDeviceDispatcher(
+            @NonNull ICommunicationDeviceDispatcher dispatcher) {
+        mCommDevDispatchers.register(dispatcher);
+    }
+
+    /*package*/ void unregisterCommunicationDeviceDispatcher(
+            @NonNull ICommunicationDeviceDispatcher dispatcher) {
+        mCommDevDispatchers.unregister(dispatcher);
+    }
+
+    // Monitoring of communication device
+    final RemoteCallbackList<ICommunicationDeviceDispatcher> mCommDevDispatchers =
+            new RemoteCallbackList<ICommunicationDeviceDispatcher>();
+
+    // portId of the device currently selected for communication: avoids broadcasting changes
+    // when same communication route is applied
+    @GuardedBy("mDeviceStateLock")
+    int mCurCommunicationPortId = -1;
+
+    @GuardedBy("mDeviceStateLock")
+    private void dispatchCommunicationDevice() {
+        AudioDeviceInfo device = getDeviceForCommunication();
+        int portId = (getDeviceForCommunication() == null) ? 0 : device.getId();
+        if (portId == mCurCommunicationPortId) {
+            return;
+        }
+        mCurCommunicationPortId = portId;
+
+        final int nbDispatchers = mCommDevDispatchers.beginBroadcast();
+        for (int i = 0; i < nbDispatchers; i++) {
+            try {
+                mCommDevDispatchers.getBroadcastItem(i)
+                        .dispatchCommunicationDeviceChanged(portId);
+            } catch (RemoteException e) {
+            }
+        }
+        mCommDevDispatchers.finishBroadcast();
     }
 
     //---------------------------------------------------------------------
@@ -675,22 +882,29 @@
                 hearingAidProfile);
     }
 
-    /*package*/ void postScoClientDied(Object obj) {
-        sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj);
+    /*package*/ void postCommunicationRouteClientDied(CommunicationRouteClient client) {
+        sendLMsgNoDelay(MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED, SENDMSG_QUEUE, client);
     }
 
-    /*package*/ void postSpeakerphoneClientDied(Object obj) {
-        sendLMsgNoDelay(MSG_L_SPEAKERPHONE_CLIENT_DIED, SENDMSG_QUEUE, obj);
-    }
-
-    /*package*/ void postSaveSetPreferredDeviceForStrategy(int strategy,
-                                                           AudioDeviceAttributes device)
+    /*package*/ void postSaveSetPreferredDevicesForStrategy(int strategy,
+                                                            List<AudioDeviceAttributes> devices)
     {
-        sendILMsgNoDelay(MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY, SENDMSG_QUEUE, strategy, device);
+        sendILMsgNoDelay(MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_QUEUE, strategy, devices);
     }
 
-    /*package*/ void postSaveRemovePreferredDeviceForStrategy(int strategy) {
-        sendIMsgNoDelay(MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY, SENDMSG_QUEUE, strategy);
+    /*package*/ void postSaveRemovePreferredDevicesForStrategy(int strategy) {
+        sendIMsgNoDelay(MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_QUEUE, strategy);
+    }
+
+    /*package*/ void postSaveSetPreferredDevicesForCapturePreset(
+            int capturePreset, List<AudioDeviceAttributes> devices) {
+        sendILMsgNoDelay(
+                MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET, SENDMSG_QUEUE, capturePreset, devices);
+    }
+
+    /*package*/ void postSaveClearPreferredDevicesForCapturePreset(int capturePreset) {
+        sendIMsgNoDelay(
+                MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET, SENDMSG_QUEUE, capturePreset);
     }
 
     //---------------------------------------------------------------------
@@ -789,15 +1003,17 @@
 
         mDeviceInventory.dump(pw, prefix);
 
-        pw.println("\n" + prefix + "mForcedUseForComm: "
-                +  AudioSystem.forceUseConfigToString(mForcedUseForComm));
-        pw.println(prefix + "mForcedUseForCommExt: "
-                + AudioSystem.forceUseConfigToString(mForcedUseForCommExt));
-        pw.println(prefix + "mModeOwnerPid: " + mModeOwnerPid);
-        pw.println(prefix + "Speakerphone clients:");
-        mSpeakerphoneClients.forEach((cl) -> {
-            pw.println("  " + prefix + "pid: " + cl.getPid() + " on: "
-                        + cl.isOn() + " cb: " + cl.getBinder()); });
+        pw.println("\n" + prefix + "Communication route clients:");
+        mCommunicationRouteClients.forEach((cl) -> {
+            pw.println("  " + prefix + "pid: " + cl.getPid() + " device: "
+                        + cl.getDevice() + " cb: " + cl.getBinder()); });
+
+        pw.println("\n" + prefix + "mPreferredDeviceforComm: "
+                +  mPreferredDeviceforComm);
+        pw.println(prefix + "mCommunicationStrategyId: "
+                +  mCommunicationStrategyId);
+
+        pw.println("\n" + prefix + "mModeOwnerPid: " + mModeOwnerPid);
 
         mBtHelper.dump(pw, prefix);
     }
@@ -820,6 +1036,11 @@
                 .set(MediaMetrics.Property.FORCE_USE_MODE,
                         AudioSystem.forceUseConfigToString(config))
                 .record();
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "onSetForceUse(useCase<" + useCase + ">, config<" + config + ">, fromA2dp<"
+                    + fromA2dp + ">, eventSource<" + eventSource + ">)");
+        }
         AudioSystem.setForceUse(useCase, config);
     }
 
@@ -885,9 +1106,13 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_RESTORE_DEVICES:
-                    synchronized (mDeviceStateLock) {
-                        mDeviceInventory.onRestoreDevices();
-                        mBtHelper.onAudioServerDiedRestoreA2dp();
+                    synchronized (mSetModeLock) {
+                        synchronized (mDeviceStateLock) {
+                            initCommunicationStrategyId();
+                            mDeviceInventory.onRestoreDevices();
+                            mBtHelper.onAudioServerDiedRestoreA2dp();
+                            onUpdateCommunicationRoute("MSG_RESTORE_DEVICES");
+                        }
                     }
                     break;
                 case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
@@ -977,25 +1202,24 @@
                             if (mModeOwnerPid != msg.arg1) {
                                 mModeOwnerPid = msg.arg1;
                                 if (msg.arg2 != AudioSystem.MODE_RINGTONE) {
-                                    updateSpeakerphoneOn("setNewModeOwner");
-                                }
-                                if (mModeOwnerPid != 0) {
-                                    mBtHelper.disconnectBluetoothSco(mModeOwnerPid);
+                                    onUpdateCommunicationRoute("setNewModeOwner");
                                 }
                             }
                         }
                     }
                     break;
-                case MSG_L_SCOCLIENT_DIED:
+                case MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED:
                     synchronized (mSetModeLock) {
                         synchronized (mDeviceStateLock) {
-                            mBtHelper.scoClientDied(msg.obj);
+                            onCommunicationRouteClientDied((CommunicationRouteClient) msg.obj);
                         }
                     }
                     break;
-                case MSG_L_SPEAKERPHONE_CLIENT_DIED:
-                    synchronized (mDeviceStateLock) {
-                        speakerphoneClientDied(msg.obj);
+                case MSG_L_UPDATE_COMMUNICATION_ROUTE:
+                    synchronized (mSetModeLock) {
+                        synchronized (mDeviceStateLock) {
+                            onUpdateCommunicationRoute((String) msg.obj);
+                        }
                     }
                     break;
                 case MSG_TOGGLE_HDMI:
@@ -1084,18 +1308,41 @@
                                 info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice);
                     }
                 } break;
-                case MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY: {
+                case MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY: {
                     final int strategy = msg.arg1;
-                    final AudioDeviceAttributes device = (AudioDeviceAttributes) msg.obj;
-                    mDeviceInventory.onSaveSetPreferredDevice(strategy, device);
+                    final List<AudioDeviceAttributes> devices =
+                            (List<AudioDeviceAttributes>) msg.obj;
+                    mDeviceInventory.onSaveSetPreferredDevices(strategy, devices);
                 } break;
-                case MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY: {
+                case MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY: {
                     final int strategy = msg.arg1;
-                    mDeviceInventory.onSaveRemovePreferredDevice(strategy);
+                    mDeviceInventory.onSaveRemovePreferredDevices(strategy);
+                } break;
+                case MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY: {
+                    final int strategy = msg.arg1;
+                    final List<AudioDeviceAttributes> devices =
+                            (List<AudioDeviceAttributes>) msg.obj;
+                    setPreferredDevicesForStrategySync(strategy, devices);
+
+                } break;
+                case MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY: {
+                    final int strategy = msg.arg1;
+                    removePreferredDevicesForStrategySync(strategy);
                 } break;
                 case MSG_CHECK_MUTE_MUSIC:
                     checkMessagesMuteMusic(0);
                     break;
+                case MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET: {
+                    final int capturePreset = msg.arg1;
+                    final List<AudioDeviceAttributes> devices =
+                            (List<AudioDeviceAttributes>) msg.obj;
+                    mDeviceInventory.onSaveSetPreferredDevicesForCapturePreset(
+                            capturePreset, devices);
+                } break;
+                case MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET: {
+                    final int capturePreset = msg.arg1;
+                    mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset);
+                } break;
                 default:
                     Log.wtf(TAG, "Invalid message " + msg.what);
             }
@@ -1163,15 +1410,19 @@
     // process external command to (dis)connect a hearing aid device
     private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 31;
 
-    // a ScoClient died in BtHelper
-    private static final int MSG_L_SCOCLIENT_DIED = 32;
-    private static final int MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY = 33;
-    private static final int MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY = 34;
+    private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY = 32;
+    private static final int MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY = 33;
 
-    private static final int MSG_L_SPEAKERPHONE_CLIENT_DIED = 35;
-    private static final int MSG_CHECK_MUTE_MUSIC = 36;
-    private static final int MSG_REPORT_NEW_ROUTES_A2DP = 37;
+    private static final int MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED = 34;
+    private static final int MSG_CHECK_MUTE_MUSIC = 35;
+    private static final int MSG_REPORT_NEW_ROUTES_A2DP = 36;
 
+    private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET = 37;
+    private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 38;
+
+    private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE = 39;
+    private static final int MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY = 40;
+    private static final int MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY = 41;
 
     private static boolean isMessageHandledUnderWakelock(int msgId) {
         switch(msgId) {
@@ -1325,14 +1576,20 @@
         }
     }
 
-    private class SpeakerphoneClient implements IBinder.DeathRecipient {
+    // List of applications requesting a specific route for communication.
+    @GuardedBy("mDeviceStateLock")
+    private final @NonNull LinkedList<CommunicationRouteClient> mCommunicationRouteClients =
+            new LinkedList<CommunicationRouteClient>();
+
+    private class CommunicationRouteClient implements IBinder.DeathRecipient {
         private final IBinder mCb;
         private final int mPid;
-        private final boolean mOn;
-        SpeakerphoneClient(IBinder cb, int pid, boolean on) {
+        private AudioDeviceAttributes mDevice;
+
+        CommunicationRouteClient(IBinder cb, int pid, AudioDeviceAttributes device) {
             mCb = cb;
             mPid = pid;
-            mOn = on;
+            mDevice = device;
         }
 
         public boolean registerDeathRecipient() {
@@ -1341,7 +1598,7 @@
                 mCb.linkToDeath(this, 0);
                 status = true;
             } catch (RemoteException e) {
-                Log.w(TAG, "SpeakerphoneClient could not link to " + mCb + " binder death");
+                Log.w(TAG, "CommunicationRouteClient could not link to " + mCb + " binder death");
             }
             return status;
         }
@@ -1350,13 +1607,13 @@
             try {
                 mCb.unlinkToDeath(this, 0);
             } catch (NoSuchElementException e) {
-                Log.w(TAG, "SpeakerphoneClient could not not unregistered to binder");
+                Log.w(TAG, "CommunicationRouteClient could not not unregistered to binder");
             }
         }
 
         @Override
         public void binderDied() {
-            postSpeakerphoneClientDied(this);
+            postCommunicationRouteClientDied(this);
         }
 
         IBinder getBinder() {
@@ -1367,29 +1624,103 @@
             return mPid;
         }
 
-        boolean isOn() {
-            return mOn;
+        AudioDeviceAttributes getDevice() {
+            return mDevice;
+        }
+
+        boolean requestsBluetoothSco() {
+            return mDevice != null
+                    && mDevice.getType()
+                        == AudioDeviceInfo.TYPE_BLUETOOTH_SCO;
+        }
+
+        boolean requestsSpeakerphone() {
+            return mDevice != null
+                    && mDevice.getType()
+                        == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
         }
     }
 
+    // @GuardedBy("mSetModeLock")
     @GuardedBy("mDeviceStateLock")
-    private void speakerphoneClientDied(Object obj) {
-        if (obj == null) {
+    private void onCommunicationRouteClientDied(CommunicationRouteClient client) {
+        if (client == null) {
             return;
         }
         Log.w(TAG, "Speaker client died");
-        if (removeSpeakerphoneClient(((SpeakerphoneClient) obj).getBinder(), false) != null) {
-            updateSpeakerphoneOn("speakerphoneClientDied");
+        if (removeCommunicationRouteClient(client.getBinder(), false)
+                != null) {
+            onUpdateCommunicationRoute("onCommunicationRouteClientDied");
         }
     }
 
-    private SpeakerphoneClient removeSpeakerphoneClient(IBinder cb, boolean unregister) {
-        for (SpeakerphoneClient cl : mSpeakerphoneClients) {
+    /**
+     * Determines which forced usage for communication should be sent to audio policy manager
+     * as a function of current SCO audio activation state and active communication route requests.
+     * SCO audio state has the highest priority as it can result from external activation by
+     * telephony service.
+     * @return selected forced usage for communication.
+     */
+    @GuardedBy("mDeviceStateLock")
+    @Nullable private AudioDeviceAttributes getPreferredDeviceForComm() {
+        boolean btSCoOn = mBluetoothScoOn && mBtHelper.isBluetoothScoOn();
+        if (btSCoOn) {
+            // Use the SCO device known to BtHelper so that it matches exactly
+            // what has been communicated to audio policy manager. The device
+            // returned by requestedCommunicationDevice() can be a dummy SCO device if legacy
+            // APIs are used to start SCO audio.
+            AudioDeviceAttributes device = mBtHelper.getHeadsetAudioDevice();
+            if (device != null) {
+                return device;
+            }
+        }
+        AudioDeviceAttributes device = requestedCommunicationDevice();
+        if (device == null
+                || AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(device.getInternalType())) {
+            // Do not indicate BT SCO selection if SCO is requested but SCO is not ON
+            return null;
+        }
+        return device;
+    }
+
+    /**
+     * Configures audio policy manager and audio HAL according to active communication route.
+     * Always called from message Handler.
+     */
+    // @GuardedBy("mSetModeLock")
+    @GuardedBy("mDeviceStateLock")
+    private void onUpdateCommunicationRoute(String eventSource) {
+        mPreferredDeviceforComm = getPreferredDeviceForComm();
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "onUpdateCommunicationRoute, mPreferredDeviceforComm: "
+                    + mPreferredDeviceforComm + " eventSource: " + eventSource);
+        }
+
+        if (mPreferredDeviceforComm == null
+                || !AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(
+                        mPreferredDeviceforComm.getInternalType())) {
+            AudioSystem.setParameters("BT_SCO=off");
+        } else {
+            AudioSystem.setParameters("BT_SCO=on");
+        }
+        if (mPreferredDeviceforComm == null) {
+            postRemovePreferredDevicesForStrategy(mCommunicationStrategyId);
+        } else {
+            postSetPreferredDevicesForStrategy(
+                    mCommunicationStrategyId, Arrays.asList(mPreferredDeviceforComm));
+        }
+        mAudioService.postUpdateRingerModeServiceInt();
+        dispatchCommunicationDevice();
+    }
+
+    private CommunicationRouteClient removeCommunicationRouteClient(
+                    IBinder cb, boolean unregister) {
+        for (CommunicationRouteClient cl : mCommunicationRouteClients) {
             if (cl.getBinder() == cb) {
                 if (unregister) {
                     cl.unregisterDeathRecipient();
                 }
-                mSpeakerphoneClients.remove(cl);
+                mCommunicationRouteClients.remove(cl);
                 return cl;
             }
         }
@@ -1397,30 +1728,25 @@
     }
 
     @GuardedBy("mDeviceStateLock")
-    private boolean addSpeakerphoneClient(IBinder cb, int pid, boolean on) {
+    private CommunicationRouteClient addCommunicationRouteClient(
+                    IBinder cb, int pid, AudioDeviceAttributes device) {
         // always insert new request at first position
-        removeSpeakerphoneClient(cb, true);
-        SpeakerphoneClient client = new SpeakerphoneClient(cb, pid, on);
+        removeCommunicationRouteClient(cb, true);
+        CommunicationRouteClient client = new CommunicationRouteClient(cb, pid, device);
         if (client.registerDeathRecipient()) {
-            mSpeakerphoneClients.add(0, client);
-            return true;
+            mCommunicationRouteClients.add(0, client);
+            return client;
         }
-        return false;
+        return null;
     }
 
     @GuardedBy("mDeviceStateLock")
-    private SpeakerphoneClient getSpeakerphoneClientForPid(int pid) {
-        for (SpeakerphoneClient cl : mSpeakerphoneClients) {
+    private CommunicationRouteClient getCommunicationRouteClientForPid(int pid) {
+        for (CommunicationRouteClient cl : mCommunicationRouteClients) {
             if (cl.getPid() == pid) {
                 return cl;
             }
         }
         return null;
     }
-
-    // List of clients requesting speakerPhone ON
-    @GuardedBy("mDeviceStateLock")
-    private final @NonNull ArrayList<SpeakerphoneClient> mSpeakerphoneClients =
-            new ArrayList<SpeakerphoneClient>();
-
 }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 02a846e..82586b8 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -16,7 +16,6 @@
 package com.android.server.audio;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
@@ -32,7 +31,8 @@
 import android.media.AudioRoutesInfo;
 import android.media.AudioSystem;
 import android.media.IAudioRoutesObserver;
-import android.media.IStrategyPreferredDeviceDispatcher;
+import android.media.ICapturePresetDevicesRoleDispatcher;
+import android.media.IStrategyPreferredDevicesDispatcher;
 import android.media.MediaMetrics;
 import android.os.Binder;
 import android.os.RemoteCallbackList;
@@ -51,6 +51,7 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -137,7 +138,12 @@
     private final ArrayMap<Integer, String> mApmConnectedDevices = new ArrayMap<>();
 
     // List of preferred devices for strategies
-    private final ArrayMap<Integer, AudioDeviceAttributes> mPreferredDevices = new ArrayMap<>();
+    private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevices =
+            new ArrayMap<>();
+
+    // List of preferred devices of capture preset
+    private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevicesForCapturePreset =
+            new ArrayMap<>();
 
     // the wrapper for AudioSystem static methods, allows us to spy AudioSystem
     private final @NonNull AudioSystemAdapter mAudioSystem;
@@ -150,8 +156,12 @@
             new RemoteCallbackList<IAudioRoutesObserver>();
 
     // Monitoring of strategy-preferred device
-    final RemoteCallbackList<IStrategyPreferredDeviceDispatcher> mPrefDevDispatchers =
-            new RemoteCallbackList<IStrategyPreferredDeviceDispatcher>();
+    final RemoteCallbackList<IStrategyPreferredDevicesDispatcher> mPrefDevDispatchers =
+            new RemoteCallbackList<IStrategyPreferredDevicesDispatcher>();
+
+    // Monitoring of devices for role and capture preset
+    final RemoteCallbackList<ICapturePresetDevicesRoleDispatcher> mDevRoleCapturePresetDispatchers =
+            new RemoteCallbackList<ICapturePresetDevicesRoleDispatcher>();
 
     /*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) {
         mDeviceBroker = broker;
@@ -242,6 +252,9 @@
             pw.println("  " + prefix + " type:0x" + Integer.toHexString(keyType)
                     + " (" + AudioSystem.getDeviceName(keyType)
                     + ") addr:" + valueAddress); });
+        mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> {
+            pw.println("  " + prefix + "capturePreset:" + capturePreset
+                    + " devices:" + devices); });
     }
 
     //------------------------------------------------------------
@@ -265,8 +278,12 @@
             }
         }
         synchronized (mPreferredDevices) {
-            mPreferredDevices.forEach((strategy, device) -> {
-                mAudioSystem.setPreferredDeviceForStrategy(strategy, device); });
+            mPreferredDevices.forEach((strategy, devices) -> {
+                mAudioSystem.setDevicesRoleForStrategy(
+                        strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); });
+        }
+        synchronized (mPreferredDevicesForCapturePreset) {
+            // TODO: call audiosystem to restore
         }
     }
 
@@ -600,52 +617,108 @@
         mmi.record();
     }
 
-    /*package*/ void onSaveSetPreferredDevice(int strategy, @NonNull AudioDeviceAttributes device) {
-        mPreferredDevices.put(strategy, device);
-        dispatchPreferredDevice(strategy, device);
+    /*package*/ void onSaveSetPreferredDevices(int strategy,
+                                               @NonNull List<AudioDeviceAttributes> devices) {
+        mPreferredDevices.put(strategy, devices);
+        dispatchPreferredDevice(strategy, devices);
     }
 
-    /*package*/ void onSaveRemovePreferredDevice(int strategy) {
+    /*package*/ void onSaveRemovePreferredDevices(int strategy) {
         mPreferredDevices.remove(strategy);
-        dispatchPreferredDevice(strategy, null);
+        dispatchPreferredDevice(strategy, new ArrayList<AudioDeviceAttributes>());
+    }
+
+    /*package*/ void onSaveSetPreferredDevicesForCapturePreset(
+            int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
+        mPreferredDevicesForCapturePreset.put(capturePreset, devices);
+        dispatchDevicesRoleForCapturePreset(
+                capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
+    }
+
+    /*package*/ void onSaveClearPreferredDevicesForCapturePreset(int capturePreset) {
+        mPreferredDevicesForCapturePreset.remove(capturePreset);
+        dispatchDevicesRoleForCapturePreset(
+                capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED,
+                new ArrayList<AudioDeviceAttributes>());
     }
 
     //------------------------------------------------------------
     //
 
-    /*package*/ int setPreferredDeviceForStrategySync(int strategy,
-                                                      @NonNull AudioDeviceAttributes device) {
+    /*package*/ int setPreferredDevicesForStrategySync(int strategy,
+            @NonNull List<AudioDeviceAttributes> devices) {
         final long identity = Binder.clearCallingIdentity();
-        final int status = mAudioSystem.setPreferredDeviceForStrategy(strategy, device);
+
+        AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
+                                "setPreferredDevicesForStrategySync, strategy: " + strategy
+                                + " devices: " + devices)).printLog(TAG));
+        final int status = mAudioSystem.setDevicesRoleForStrategy(
+                strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
         Binder.restoreCallingIdentity(identity);
 
         if (status == AudioSystem.SUCCESS) {
-            mDeviceBroker.postSaveSetPreferredDeviceForStrategy(strategy, device);
+            mDeviceBroker.postSaveSetPreferredDevicesForStrategy(strategy, devices);
         }
         return status;
     }
 
-    /*package*/ int removePreferredDeviceForStrategySync(int strategy) {
+    /*package*/ int removePreferredDevicesForStrategySync(int strategy) {
         final long identity = Binder.clearCallingIdentity();
-        final int status = mAudioSystem.removePreferredDeviceForStrategy(strategy);
+        final int status = mAudioSystem.removeDevicesRoleForStrategy(
+                strategy, AudioSystem.DEVICE_ROLE_PREFERRED);
         Binder.restoreCallingIdentity(identity);
 
         if (status == AudioSystem.SUCCESS) {
-            mDeviceBroker.postSaveRemovePreferredDeviceForStrategy(strategy);
+            mDeviceBroker.postSaveRemovePreferredDevicesForStrategy(strategy);
         }
         return status;
     }
 
-    /*package*/ void registerStrategyPreferredDeviceDispatcher(
-            @NonNull IStrategyPreferredDeviceDispatcher dispatcher) {
+    /*package*/ void registerStrategyPreferredDevicesDispatcher(
+            @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
         mPrefDevDispatchers.register(dispatcher);
     }
 
-    /*package*/ void unregisterStrategyPreferredDeviceDispatcher(
-            @NonNull IStrategyPreferredDeviceDispatcher dispatcher) {
+    /*package*/ void unregisterStrategyPreferredDevicesDispatcher(
+            @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
         mPrefDevDispatchers.unregister(dispatcher);
     }
 
+    /*package*/ int setPreferredDevicesForCapturePresetSync(
+            int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
+        final long identity = Binder.clearCallingIdentity();
+        final int status = mAudioSystem.setDevicesRoleForCapturePreset(
+                capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
+        Binder.restoreCallingIdentity(identity);
+
+        if (status == AudioSystem.SUCCESS) {
+            mDeviceBroker.postSaveSetPreferredDevicesForCapturePreset(capturePreset, devices);
+        }
+        return status;
+    }
+
+    /*package*/ int clearPreferredDevicesForCapturePresetSync(int capturePreset) {
+        final long identity = Binder.clearCallingIdentity();
+        final int status = mAudioSystem.clearDevicesRoleForCapturePreset(
+                capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED);
+        Binder.restoreCallingIdentity(identity);
+
+        if (status == AudioSystem.SUCCESS) {
+            mDeviceBroker.postSaveClearPreferredDevicesForCapturePreset(capturePreset);
+        }
+        return status;
+    }
+
+    /*package*/ void registerCapturePresetDevicesRoleDispatcher(
+            @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
+        mDevRoleCapturePresetDispatchers.register(dispatcher);
+    }
+
+    /*package*/ void unregisterCapturePresetDevicesRoleDispatcher(
+            @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
+        mDevRoleCapturePresetDispatchers.unregister(dispatcher);
+    }
+
     /**
      * Implements the communication with AudioSystem to (dis)connect a device in the native layers
      * @param connect true if connection
@@ -1288,17 +1361,32 @@
         }
     }
 
-    private void dispatchPreferredDevice(int strategy, @Nullable AudioDeviceAttributes device) {
+    private void dispatchPreferredDevice(int strategy,
+                                         @NonNull List<AudioDeviceAttributes> devices) {
         final int nbDispatchers = mPrefDevDispatchers.beginBroadcast();
         for (int i = 0; i < nbDispatchers; i++) {
             try {
-                mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDeviceChanged(strategy, device);
+                mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDevicesChanged(
+                        strategy, devices);
             } catch (RemoteException e) {
             }
         }
         mPrefDevDispatchers.finishBroadcast();
     }
 
+    private void dispatchDevicesRoleForCapturePreset(
+            int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
+        final int nbDispatchers = mDevRoleCapturePresetDispatchers.beginBroadcast();
+        for (int i = 0; i < nbDispatchers; ++i) {
+            try {
+                mDevRoleCapturePresetDispatchers.getBroadcastItem(i).dispatchDevicesRoleChanged(
+                        capturePreset, role, devices);
+            } catch (RemoteException e) {
+            }
+        }
+        mDevRoleCapturePresetDispatchers.finishBroadcast();
+    }
+
     //----------------------------------------------------------
     // For tests only
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index a06f9dd..024dca7 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -84,10 +84,12 @@
 import android.media.IAudioRoutesObserver;
 import android.media.IAudioServerStateDispatcher;
 import android.media.IAudioService;
+import android.media.ICapturePresetDevicesRoleDispatcher;
+import android.media.ICommunicationDeviceDispatcher;
 import android.media.IPlaybackConfigDispatcher;
 import android.media.IRecordingConfigDispatcher;
 import android.media.IRingtonePlayer;
-import android.media.IStrategyPreferredDeviceDispatcher;
+import android.media.IStrategyPreferredDevicesDispatcher;
 import android.media.IVolumeController;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
@@ -171,6 +173,7 @@
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
 
 /**
  * The implementation of the audio service for volume, audio focus, device management...
@@ -205,6 +208,9 @@
     /** debug calls to devices APIs */
     protected static final boolean DEBUG_DEVICES = false;
 
+    /** Debug communication route */
+    protected static final boolean DEBUG_COMM_RTE = false;
+
     /** How long to delay before persisting a change in volume/ringer mode. */
     private static final int PERSIST_DELAY = 500;
 
@@ -1850,22 +1856,28 @@
     ///////////////////////////////////////////////////////////////////////////
     // IPC methods
     ///////////////////////////////////////////////////////////////////////////
-    /** @see AudioManager#setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceInfo) */
-    public int setPreferredDeviceForStrategy(int strategy, AudioDeviceAttributes device) {
-        if (device == null) {
+    /**
+     * @see AudioManager#setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)
+     * @see AudioManager#setPreferredDevicesForStrategy(AudioProductStrategy,
+     *                                                  List<AudioDeviceAttributes>)
+     */
+    public int setPreferredDevicesForStrategy(int strategy, List<AudioDeviceAttributes> devices) {
+        if (devices == null) {
             return AudioSystem.ERROR;
         }
         enforceModifyAudioRoutingPermission();
         final String logString = String.format(
                 "setPreferredDeviceForStrategy u/pid:%d/%d strat:%d dev:%s",
-                Binder.getCallingUid(), Binder.getCallingPid(), strategy, device.toString());
+                Binder.getCallingUid(), Binder.getCallingPid(), strategy,
+                devices.stream().map(e -> e.toString()).collect(Collectors.joining(",")));
         sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
-        if (device.getRole() == AudioDeviceAttributes.ROLE_INPUT) {
+        if (devices.stream().anyMatch(device ->
+                device.getRole() == AudioDeviceAttributes.ROLE_INPUT)) {
             Log.e(TAG, "Unsupported input routing in " + logString);
             return AudioSystem.ERROR;
         }
 
-        final int status = mDeviceBroker.setPreferredDeviceForStrategySync(strategy, device);
+        final int status = mDeviceBroker.setPreferredDevicesForStrategySync(strategy, devices);
         if (status != AudioSystem.SUCCESS) {
             Log.e(TAG, String.format("Error %d in %s)", status, logString));
         }
@@ -1874,53 +1886,149 @@
     }
 
     /** @see AudioManager#removePreferredDeviceForStrategy(AudioProductStrategy) */
-    public int removePreferredDeviceForStrategy(int strategy) {
+    public int removePreferredDevicesForStrategy(int strategy) {
         enforceModifyAudioRoutingPermission();
         final String logString =
                 String.format("removePreferredDeviceForStrategy strat:%d", strategy);
         sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
 
-        final int status = mDeviceBroker.removePreferredDeviceForStrategySync(strategy);
+        final int status = mDeviceBroker.removePreferredDevicesForStrategySync(strategy);
         if (status != AudioSystem.SUCCESS) {
             Log.e(TAG, String.format("Error %d in %s)", status, logString));
         }
         return status;
     }
 
-    /** @see AudioManager#getPreferredDeviceForStrategy(AudioProductStrategy) */
-    public AudioDeviceAttributes getPreferredDeviceForStrategy(int strategy) {
+    /**
+     * @see AudioManager#getPreferredDeviceForStrategy(AudioProductStrategy)
+     * @see AudioManager#getPreferredDevicesForStrategy(AudioProductStrategy)
+     */
+    public List<AudioDeviceAttributes> getPreferredDevicesForStrategy(int strategy) {
         enforceModifyAudioRoutingPermission();
-        AudioDeviceAttributes[] devices = new AudioDeviceAttributes[1];
+        List<AudioDeviceAttributes> devices = new ArrayList<>();
         final long identity = Binder.clearCallingIdentity();
-        final int status = AudioSystem.getPreferredDeviceForStrategy(strategy, devices);
+        final int status = AudioSystem.getDevicesForRoleAndStrategy(
+                strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
         Binder.restoreCallingIdentity(identity);
         if (status != AudioSystem.SUCCESS) {
             Log.e(TAG, String.format("Error %d in getPreferredDeviceForStrategy(%d)",
                     status, strategy));
-            return null;
+            return new ArrayList<AudioDeviceAttributes>();
         } else {
-            return devices[0];
+            return devices;
         }
     }
 
-    /** @see AudioManager#addOnPreferredDeviceForStrategyChangedListener(Executor, AudioManager.OnPreferredDeviceForStrategyChangedListener) */
-    public void registerStrategyPreferredDeviceDispatcher(
-            @Nullable IStrategyPreferredDeviceDispatcher dispatcher) {
+    /** @see AudioManager#addOnPreferredDevicesForStrategyChangedListener(
+     *               Executor, AudioManager.OnPreferredDevicesForStrategyChangedListener)
+     */
+    public void registerStrategyPreferredDevicesDispatcher(
+            @Nullable IStrategyPreferredDevicesDispatcher dispatcher) {
         if (dispatcher == null) {
             return;
         }
         enforceModifyAudioRoutingPermission();
-        mDeviceBroker.registerStrategyPreferredDeviceDispatcher(dispatcher);
+        mDeviceBroker.registerStrategyPreferredDevicesDispatcher(dispatcher);
     }
 
-    /** @see AudioManager#removeOnPreferredDeviceForStrategyChangedListener(AudioManager.OnPreferredDeviceForStrategyChangedListener) */
-    public void unregisterStrategyPreferredDeviceDispatcher(
-            @Nullable IStrategyPreferredDeviceDispatcher dispatcher) {
+    /** @see AudioManager#removeOnPreferredDevicesForStrategyChangedListener(
+     *               AudioManager.OnPreferredDevicesForStrategyChangedListener)
+     */
+    public void unregisterStrategyPreferredDevicesDispatcher(
+            @Nullable IStrategyPreferredDevicesDispatcher dispatcher) {
         if (dispatcher == null) {
             return;
         }
         enforceModifyAudioRoutingPermission();
-        mDeviceBroker.unregisterStrategyPreferredDeviceDispatcher(dispatcher);
+        mDeviceBroker.unregisterStrategyPreferredDevicesDispatcher(dispatcher);
+    }
+
+    /**
+     * @see AudioManager#setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)
+     */
+    public int setPreferredDevicesForCapturePreset(
+            int capturePreset, List<AudioDeviceAttributes> devices) {
+        if (devices == null) {
+            return AudioSystem.ERROR;
+        }
+        enforceModifyAudioRoutingPermission();
+        final String logString = String.format(
+                "setPreferredDevicesForCapturePreset u/pid:%d/%d source:%d dev:%s",
+                Binder.getCallingUid(), Binder.getCallingPid(), capturePreset,
+                devices.stream().map(e -> e.toString()).collect(Collectors.joining(",")));
+        sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
+        if (devices.stream().anyMatch(device ->
+                device.getRole() == AudioDeviceAttributes.ROLE_OUTPUT)) {
+            Log.e(TAG, "Unsupported output routing in " + logString);
+            return AudioSystem.ERROR;
+        }
+
+        final int status = mDeviceBroker.setPreferredDevicesForCapturePresetSync(
+                capturePreset, devices);
+        if (status != AudioSystem.SUCCESS) {
+            Log.e(TAG, String.format("Error %d in %s)", status, logString));
+        }
+
+        return status;
+    }
+
+    /** @see AudioManager#clearPreferredDevicesForCapturePreset(int) */
+    public int clearPreferredDevicesForCapturePreset(int capturePreset) {
+        enforceModifyAudioRoutingPermission();
+        final String logString = String.format(
+                "removePreferredDeviceForCapturePreset source:%d", capturePreset);
+        sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
+
+        final int status = mDeviceBroker.clearPreferredDevicesForCapturePresetSync(capturePreset);
+        if (status != AudioSystem.SUCCESS) {
+            Log.e(TAG, String.format("Error %d in %s", status, logString));
+        }
+        return status;
+    }
+
+    /**
+     * @see AudioManager#getPreferredDevicesForCapturePreset(int)
+     */
+    public List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int capturePreset) {
+        enforceModifyAudioRoutingPermission();
+        List<AudioDeviceAttributes> devices = new ArrayList<>();
+        final long identity = Binder.clearCallingIdentity();
+        final int status = AudioSystem.getDevicesForRoleAndCapturePreset(
+                capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
+        Binder.restoreCallingIdentity(identity);
+        if (status != AudioSystem.SUCCESS) {
+            Log.e(TAG, String.format("Error %d in getPreferredDeviceForCapturePreset(%d)",
+                    status, capturePreset));
+            return new ArrayList<AudioDeviceAttributes>();
+        } else {
+            return devices;
+        }
+    }
+
+    /**
+     * @see AudioManager#addOnPreferredDevicesForCapturePresetChangedListener(
+     *              Executor, OnPreferredDevicesForCapturePresetChangedListener)
+     */
+    public void registerCapturePresetDevicesRoleDispatcher(
+            @Nullable ICapturePresetDevicesRoleDispatcher dispatcher) {
+        if (dispatcher == null) {
+            return;
+        }
+        enforceModifyAudioRoutingPermission();
+        mDeviceBroker.registerCapturePresetDevicesRoleDispatcher(dispatcher);
+    }
+
+    /**
+     * @see AudioManager#removeOnPreferredDevicesForCapturePresetChangedListener(
+     *              AudioManager.OnPreferredDevicesForCapturePresetChangedListener)
+     */
+    public void unregisterCapturePresetDevicesRoleDispatcher(
+            @Nullable ICapturePresetDevicesRoleDispatcher dispatcher) {
+        if (dispatcher == null) {
+            return;
+        }
+        enforceModifyAudioRoutingPermission();
+        mDeviceBroker.unregisterCapturePresetDevicesRoleDispatcher(dispatcher);
     }
 
     /** @see AudioManager#getDevicesForAttributes(AudioAttributes) */
@@ -3585,7 +3693,7 @@
         final boolean ringerModeMute = ringerMode == AudioManager.RINGER_MODE_VIBRATE
                 || ringerMode == AudioManager.RINGER_MODE_SILENT;
         final boolean shouldRingSco = ringerMode == AudioManager.RINGER_MODE_VIBRATE
-                && isBluetoothScoOn();
+                && mDeviceBroker.isBluetoothScoOn();
         // Ask audio policy engine to force use Bluetooth SCO channel if needed
         final String eventSource = "muteRingerModeStreams() from u/pid:" + Binder.getCallingUid()
                 + "/" + Binder.getCallingPid();
@@ -4161,6 +4269,115 @@
         restoreDeviceVolumeBehavior();
     }
 
+    private static final int[] VALID_COMMUNICATION_DEVICE_TYPES = {
+        AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
+        AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
+        AudioDeviceInfo.TYPE_WIRED_HEADSET,
+        AudioDeviceInfo.TYPE_USB_HEADSET,
+        AudioDeviceInfo.TYPE_BUILTIN_EARPIECE,
+        AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
+        AudioDeviceInfo.TYPE_HEARING_AID,
+        AudioDeviceInfo.TYPE_BLE_HEADSET,
+        AudioDeviceInfo.TYPE_USB_DEVICE,
+        AudioDeviceInfo.TYPE_BLE_SPEAKER,
+        AudioDeviceInfo.TYPE_LINE_ANALOG,
+        AudioDeviceInfo.TYPE_HDMI,
+        AudioDeviceInfo.TYPE_AUX_LINE
+    };
+
+    private boolean isValidCommunicationDevice(AudioDeviceInfo device) {
+        for (int type : VALID_COMMUNICATION_DEVICE_TYPES) {
+            if (device.getType() == type) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** @see AudioManager#setDeviceForCommunication(int) */
+    public boolean setDeviceForCommunication(IBinder cb, int portId) {
+        final int uid = Binder.getCallingUid();
+        final int pid = Binder.getCallingPid();
+
+        AudioDeviceInfo device = null;
+        if (portId != 0) {
+            device = AudioManager.getDeviceForPortId(portId, AudioManager.GET_DEVICES_OUTPUTS);
+            if (device == null) {
+                throw new IllegalArgumentException("invalid portID " + portId);
+            }
+            if (!isValidCommunicationDevice(device)) {
+                throw new IllegalArgumentException("invalid device type " + device.getType());
+            }
+        }
+        final String eventSource = new StringBuilder("setDeviceForCommunication(")
+                .append(") from u/pid:").append(uid).append("/")
+                .append(pid).toString();
+
+        int deviceType = AudioSystem.DEVICE_OUT_DEFAULT;
+        String deviceAddress = null;
+        if (device != null) {
+            deviceType = device.getPort().type();
+            deviceAddress = device.getAddress();
+        } else {
+            AudioDeviceInfo curDevice = mDeviceBroker.getDeviceForCommunication();
+            if (curDevice != null) {
+                deviceType = curDevice.getPort().type();
+                deviceAddress = curDevice.getAddress();
+            }
+        }
+        // do not log metrics if clearing communication device while no communication device
+        // was selected
+        if (deviceType != AudioSystem.DEVICE_OUT_DEFAULT) {
+            new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE
+                    + MediaMetrics.SEPARATOR + "setDeviceForCommunication")
+                    .set(MediaMetrics.Property.DEVICE,
+                            AudioSystem.getDeviceName(deviceType))
+                    .set(MediaMetrics.Property.ADDRESS, deviceAddress)
+                    .set(MediaMetrics.Property.STATE, device != null
+                            ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED)
+                    .record();
+        }
+
+        final long ident = Binder.clearCallingIdentity();
+        boolean status =
+                mDeviceBroker.setDeviceForCommunication(cb, pid, device, eventSource);
+        Binder.restoreCallingIdentity(ident);
+        return status;
+    }
+
+    /** @see AudioManager#getDeviceForCommunication() */
+    public int getDeviceForCommunication() {
+        final long ident = Binder.clearCallingIdentity();
+        AudioDeviceInfo device = mDeviceBroker.getDeviceForCommunication();
+        Binder.restoreCallingIdentity(ident);
+        if (device == null) {
+            return 0;
+        }
+        return device.getId();
+    }
+
+    /** @see AudioManager#addOnCommunicationDeviceChangedListener(
+     *               Executor, AudioManager.OnCommunicationDeviceChangedListener)
+     */
+    public void registerCommunicationDeviceDispatcher(
+            @Nullable ICommunicationDeviceDispatcher dispatcher) {
+        if (dispatcher == null) {
+            return;
+        }
+        mDeviceBroker.registerCommunicationDeviceDispatcher(dispatcher);
+    }
+
+    /** @see AudioManager#removeOnCommunicationDeviceChangedListener(
+     *               AudioManager.OnCommunicationDeviceChangedListener)
+     */
+    public void unregisterCommunicationDeviceDispatcher(
+            @Nullable ICommunicationDeviceDispatcher dispatcher) {
+        if (dispatcher == null) {
+            return;
+        }
+        mDeviceBroker.unregisterCommunicationDeviceDispatcher(dispatcher);
+    }
+
     /** @see AudioManager#setSpeakerphoneOn(boolean) */
     public void setSpeakerphoneOn(IBinder cb, boolean on) {
         if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
@@ -4170,10 +4387,10 @@
         // for logging only
         final int uid = Binder.getCallingUid();
         final int pid = Binder.getCallingPid();
+
         final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on)
                 .append(") from u/pid:").append(uid).append("/")
                 .append(pid).toString();
-        final boolean stateChanged = mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
         new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE
                 + MediaMetrics.SEPARATOR + "setSpeakerphoneOn")
                 .setUid(uid)
@@ -4181,17 +4398,9 @@
                 .set(MediaMetrics.Property.STATE, on
                         ? MediaMetrics.Value.ON : MediaMetrics.Value.OFF)
                 .record();
-
-        if (stateChanged) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                mContext.sendBroadcastAsUser(
-                        new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
-                                .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
+        final long ident = Binder.clearCallingIdentity();
+        mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
+        Binder.restoreCallingIdentity(ident);
     }
 
     /** @see AudioManager#isSpeakerphoneOn() */
@@ -4199,6 +4408,11 @@
         return mDeviceBroker.isSpeakerphoneOn();
     }
 
+
+    /** BT SCO audio state seen by apps using the deprecated API setBluetoothScoOn().
+     * @see isBluetoothScoOn() */
+    private boolean mBtScoOnByApp;
+
     /** @see AudioManager#setBluetoothScoOn(boolean) */
     public void setBluetoothScoOn(boolean on) {
         if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
@@ -4207,7 +4421,7 @@
 
         // Only enable calls from system components
         if (UserHandle.getCallingAppId() >= FIRST_APPLICATION_UID) {
-            mDeviceBroker.setBluetoothScoOnByApp(on);
+            mBtScoOnByApp = on;
             return;
         }
 
@@ -4233,7 +4447,7 @@
      * Note that it doesn't report internal state, but state seen by apps (which may have
      * called setBluetoothScoOn() */
     public boolean isBluetoothScoOn() {
-        return mDeviceBroker.isBluetoothScoOnForApp();
+        return mBtScoOnByApp || mDeviceBroker.isBluetoothScoOn();
     }
 
     // TODO investigate internal users due to deprecation of SDK API
@@ -4280,7 +4494,7 @@
                 .set(MediaMetrics.Property.SCO_AUDIO_MODE,
                         BtHelper.scoAudioModeToString(scoAudioMode))
                 .record();
-        startBluetoothScoInt(cb, scoAudioMode, eventSource);
+        startBluetoothScoInt(cb, pid, scoAudioMode, eventSource);
 
     }
 
@@ -4299,10 +4513,10 @@
                 .set(MediaMetrics.Property.SCO_AUDIO_MODE,
                         BtHelper.scoAudioModeToString(BtHelper.SCO_MODE_VIRTUAL_CALL))
                 .record();
-        startBluetoothScoInt(cb, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource);
+        startBluetoothScoInt(cb, pid, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource);
     }
 
-    void startBluetoothScoInt(IBinder cb, int scoAudioMode, @NonNull String eventSource) {
+    void startBluetoothScoInt(IBinder cb, int pid, int scoAudioMode, @NonNull String eventSource) {
         MediaMetrics.Item mmi = new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
                 .set(MediaMetrics.Property.EVENT, "startBluetoothScoInt")
                 .set(MediaMetrics.Property.SCO_AUDIO_MODE,
@@ -4313,9 +4527,9 @@
             mmi.set(MediaMetrics.Property.EARLY_RETURN, "permission or systemReady").record();
             return;
         }
-        synchronized (mDeviceBroker.mSetModeLock) {
-            mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource);
-        }
+        final long ident = Binder.clearCallingIdentity();
+        mDeviceBroker.startBluetoothScoForClient(cb, pid, scoAudioMode, eventSource);
+        Binder.restoreCallingIdentity(ident);
         mmi.record();
     }
 
@@ -4330,9 +4544,9 @@
         final String eventSource =  new StringBuilder("stopBluetoothSco()")
                 .append(") from u/pid:").append(uid).append("/")
                 .append(pid).toString();
-        synchronized (mDeviceBroker.mSetModeLock) {
-            mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource);
-        }
+        final long ident = Binder.clearCallingIdentity();
+        mDeviceBroker.stopBluetoothScoForClient(cb, pid, eventSource);
+        Binder.restoreCallingIdentity(ident);
         new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
                 .setUid(uid)
                 .setPid(pid)
@@ -4758,8 +4972,7 @@
         switch (mPlatformType) {
         case AudioSystem.PLATFORM_VOICE:
             if (isInCommunication()) {
-                if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
-                        == AudioSystem.FORCE_BT_SCO) {
+                if (mDeviceBroker.isBluetoothScoOn()) {
                     // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
                     return AudioSystem.STREAM_BLUETOOTH_SCO;
                 } else {
@@ -4795,8 +5008,7 @@
             }
         default:
             if (isInCommunication()) {
-                if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
-                        == AudioSystem.FORCE_BT_SCO) {
+                if (mDeviceBroker.isBluetoothScoOn()) {
                     if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
                     return AudioSystem.STREAM_BLUETOOTH_SCO;
                 } else {
@@ -7566,6 +7778,7 @@
         pw.print("  mHasVibrator="); pw.println(mHasVibrator);
         pw.print("  mVolumePolicy="); pw.println(mVolumePolicy);
         pw.print("  mAvrcpAbsVolSupported="); pw.println(mAvrcpAbsVolSupported);
+        pw.print("  mBtScoOnByApp="); pw.println(mBtScoOnByApp);
         pw.print("  mIsSingleVolume="); pw.println(mIsSingleVolume);
         pw.print("  mUseFixedVolume="); pw.println(mUseFixedVolume);
         pw.print("  mFixedVolumeDevices="); pw.println(dumpDeviceTypes(mFixedVolumeDevices));
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index e60243f..ae64990 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -20,6 +20,8 @@
 import android.media.AudioDeviceAttributes;
 import android.media.AudioSystem;
 
+import java.util.List;
+
 /**
  * Provides an adapter to access functionality of the android.media.AudioSystem class for device
  * related functionality.
@@ -77,22 +79,59 @@
     }
 
     /**
-     * Same as {@link AudioSystem#setPreferredDeviceForStrategy(int, AudioDeviceAttributes)}
+     * Same as {@link AudioSystem#setDevicesRoleForStrategy(int, int, List)}
      * @param strategy
-     * @param device
+     * @param role
+     * @param devices
      * @return
      */
-    public int setPreferredDeviceForStrategy(int strategy, @NonNull AudioDeviceAttributes device) {
-        return AudioSystem.setPreferredDeviceForStrategy(strategy, device);
+    public int setDevicesRoleForStrategy(int strategy, int role,
+                                         @NonNull List<AudioDeviceAttributes> devices) {
+        return AudioSystem.setDevicesRoleForStrategy(strategy, role, devices);
     }
 
     /**
-     * Same as {@link AudioSystem#removePreferredDeviceForStrategy(int)}
+     * Same as {@link AudioSystem#removeDevicesRoleForStrategy(int, int)}
      * @param strategy
+     * @param role
      * @return
      */
-    public int removePreferredDeviceForStrategy(int strategy) {
-        return AudioSystem.removePreferredDeviceForStrategy(strategy);
+    public int removeDevicesRoleForStrategy(int strategy, int role) {
+        return AudioSystem.removeDevicesRoleForStrategy(strategy, role);
+    }
+
+    /**
+     * Same as (@link AudioSystem#setDevicesRoleForCapturePreset(int, List))
+     * @param capturePreset
+     * @param role
+     * @param devices
+     * @return
+     */
+    public int setDevicesRoleForCapturePreset(int capturePreset, int role,
+                                              @NonNull List<AudioDeviceAttributes> devices) {
+        return AudioSystem.setDevicesRoleForCapturePreset(capturePreset, role, devices);
+    }
+
+    /**
+     * Same as {@link AudioSystem#removeDevicesRoleForCapturePreset(int, int)}
+     * @param capturePreset
+     * @param role
+     * @param devicesToRemove
+     * @return
+     */
+    public int removeDevicesRoleForCapturePreset(
+            int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devicesToRemove) {
+        return AudioSystem.removeDevicesRoleForCapturePreset(capturePreset, role, devicesToRemove);
+    }
+
+    /**
+     * Same as {@link AudioSystem#}
+     * @param capturePreset
+     * @param role
+     * @return
+     */
+    public int clearDevicesRoleForCapturePreset(int capturePreset, int role) {
+        return AudioSystem.clearDevicesRoleForCapturePreset(capturePreset, role);
     }
 
     /**
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 7616557..c9a1fcf 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -27,11 +27,10 @@
 import android.bluetooth.BluetoothHearingAid;
 import android.bluetooth.BluetoothProfile;
 import android.content.Intent;
+import android.media.AudioDeviceAttributes;
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.os.Binder;
-import android.os.IBinder;
-import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
@@ -39,9 +38,7 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.List;
-import java.util.NoSuchElementException;
 import java.util.Objects;
 
 /**
@@ -58,10 +55,6 @@
         mDeviceBroker = broker;
     }
 
-    // List of clients having issued a SCO start request
-    @GuardedBy("BtHelper.this")
-    private final @NonNull ArrayList<ScoClient> mScoClients = new ArrayList<ScoClient>();
-
     // BluetoothHeadset API to control SCO connection
     private @Nullable BluetoothHeadset mBluetoothHeadset;
 
@@ -301,6 +294,8 @@
     @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     /*package*/ synchronized void receiveBtEvent(Intent intent) {
         final String action = intent.getAction();
+
+        Log.i(TAG, "receiveBtEvent action: " + action + " mScoAudioState: " + mScoAudioState);
         if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
             BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
             setBtScoActiveDevice(btDevice);
@@ -308,20 +303,16 @@
             boolean broadcast = false;
             int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
             int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
-            // broadcast intent if the connection was initated by AudioService
-            if (!mScoClients.isEmpty()
-                    && (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL
-                    || mScoAudioState == SCO_STATE_ACTIVATE_REQ
-                    || mScoAudioState == SCO_STATE_DEACTIVATE_REQ
-                    || mScoAudioState == SCO_STATE_DEACTIVATING)) {
-                broadcast = true;
-            }
+            Log.i(TAG, "receiveBtEvent ACTION_AUDIO_STATE_CHANGED: " + btState);
             switch (btState) {
                 case BluetoothHeadset.STATE_AUDIO_CONNECTED:
                     scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
                     if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
                             && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
                         mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
+                    } else if (mDeviceBroker.isBluetoothScoRequested()) {
+                        // broadcast intent if the connection was initated by AudioService
+                        broadcast = true;
                     }
                     mDeviceBroker.setBluetoothScoOn(true, "BtHelper.receiveBtEvent");
                     break;
@@ -333,21 +324,21 @@
                     // notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ.
                     // 2) If audio was connected then disconnected via Bluetooth APIs and
                     // we still have pending activation requests by apps: this is indicated by
-                    // state SCO_STATE_ACTIVE_EXTERNAL and the mScoClients list not empty.
+                    // state SCO_STATE_ACTIVE_EXTERNAL and BT SCO is requested.
                     if (mScoAudioState == SCO_STATE_ACTIVATE_REQ
                             || (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL
-                                    && !mScoClients.isEmpty())) {
+                                    && mDeviceBroker.isBluetoothScoRequested())) {
                         if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
                                 && connectBluetoothScoAudioHelper(mBluetoothHeadset,
                                 mBluetoothHeadsetDevice, mScoAudioMode)) {
                             mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
-                            broadcast = false;
+                            scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTING;
+                            broadcast = true;
                             break;
                         }
                     }
-                    // Tear down SCO if disconnected from external
-                    if (mScoAudioState == SCO_STATE_DEACTIVATING) {
-                        clearAllScoClients(0, false);
+                    if (mScoAudioState != SCO_STATE_ACTIVE_EXTERNAL) {
+                        broadcast = true;
                     }
                     mScoAudioState = SCO_STATE_INACTIVE;
                     break;
@@ -356,11 +347,8 @@
                             && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
                         mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
                     }
-                    broadcast = false;
                     break;
                 default:
-                    // do not broadcast CONNECTING or invalid state
-                    broadcast = false;
                     break;
             }
             if (broadcast) {
@@ -386,81 +374,19 @@
                 == BluetoothHeadset.STATE_AUDIO_CONNECTED;
     }
 
-    /**
-     * Disconnect all SCO connections started by {@link AudioManager} except those started by
-     * {@param exceptPid}
-     *
-     * @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept
-     */
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
     @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    /*package*/ synchronized void disconnectBluetoothSco(int exceptPid) {
-        checkScoAudioState();
-        if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
-            return;
-        }
-        clearAllScoClients(exceptPid, true);
-    }
-
-    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    /*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode,
+    /*package*/ synchronized boolean startBluetoothSco(int scoAudioMode,
                 @NonNull String eventSource) {
-        ScoClient client = getScoClient(cb, true);
-        // The calling identity must be cleared before calling ScoClient.incCount().
-        // inCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
-        // and this must be done on behalf of system server to make sure permissions are granted.
-        // The caller identity must be cleared after getScoClient() because it is needed if a new
-        // client is created.
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
-            client.requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
-        } catch (NullPointerException e) {
-            Log.e(TAG, "Null ScoClient", e);
-        }
-        Binder.restoreCallingIdentity(ident);
-    }
-
-    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    /*package*/ synchronized void stopBluetoothScoForClient(IBinder cb,
-            @NonNull String eventSource) {
-        ScoClient client = getScoClient(cb, false);
-        // The calling identity must be cleared before calling ScoClient.decCount().
-        // decCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
-        // and this must be done on behalf of system server to make sure permissions are granted.
-        final long ident = Binder.clearCallingIdentity();
-        if (client != null) {
-            stopAndRemoveClient(client, eventSource);
-        }
-        Binder.restoreCallingIdentity(ident);
-    }
-
-    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    /*package*/ synchronized void stopBluetoothScoForPid(int pid) {
-        ScoClient client = getScoClientForPid(pid);
-        if (client == null) {
-            return;
-        }
-        final String eventSource = new StringBuilder("stopBluetoothScoForPid(")
-                .append(pid).append(")").toString();
-        stopAndRemoveClient(client, eventSource);
-    }
-
-    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    // @GuardedBy("BtHelper.this")
-    private void stopAndRemoveClient(ScoClient client, @NonNull String eventSource) {
         AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
-        client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
-                SCO_MODE_VIRTUAL_CALL);
-        // If a disconnection is pending, the client will be removed when clearAllScoClients()
-        // is called form receiveBtEvent()
-        if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ
-                && mScoAudioState != SCO_STATE_DEACTIVATING) {
-            client.remove(false /*stop */, true /*unregister*/);
-        }
+        return requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
+    }
+
+    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+    /*package*/ synchronized boolean stopBluetoothSco(@NonNull String eventSource) {
+        AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
+        return requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, SCO_MODE_VIRTUAL_CALL);
     }
 
     /*package*/ synchronized void setHearingAidVolume(int index, int streamType) {
@@ -507,7 +433,6 @@
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
     @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     /*package*/ synchronized void resetBluetoothSco() {
-        clearAllScoClients(0, false);
         mScoAudioState = SCO_STATE_INACTIVE;
         broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
         AudioSystem.setParameters("A2dpSuspended=false");
@@ -606,46 +531,64 @@
         mDeviceBroker.postBroadcastScoConnectionState(state);
     }
 
-    private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
-        if (btDevice == null) {
-            return true;
+    @Nullable AudioDeviceAttributes getHeadsetAudioDevice() {
+        if (mBluetoothHeadsetDevice == null) {
+            return null;
         }
+        return btHeadsetDeviceToAudioDevice(mBluetoothHeadsetDevice);
+    }
+
+    private AudioDeviceAttributes btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice) {
         String address = btDevice.getAddress();
+        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
+            address = "";
+        }
         BluetoothClass btClass = btDevice.getBluetoothClass();
-        int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
-        int[] outDeviceTypes = {
-                AudioSystem.DEVICE_OUT_BLUETOOTH_SCO,
-                AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
-                AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT
-        };
+        int nativeType = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
         if (btClass != null) {
             switch (btClass.getDeviceClass()) {
                 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
                 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
-                    outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET };
+                    nativeType = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
                     break;
                 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
-                    outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT };
+                    nativeType = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
                     break;
             }
         }
-        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
-            address = "";
+        if (AudioService.DEBUG_DEVICES) {
+            Log.i(TAG, "btHeadsetDeviceToAudioDevice btDevice: " + btDevice
+                    + " btClass: " + (btClass == null ? "Unknown" : btClass)
+                    + " nativeType: " + nativeType + " address: " + address);
         }
+        return new AudioDeviceAttributes(nativeType, address);
+    }
+
+    private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
+        if (btDevice == null) {
+            return true;
+        }
+        int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
+        AudioDeviceAttributes audioDevice =  btHeadsetDeviceToAudioDevice(btDevice);
         String btDeviceName =  getName(btDevice);
         boolean result = false;
         if (isActive) {
-            result |= mDeviceBroker.handleDeviceConnection(
-                    isActive, outDeviceTypes[0], address, btDeviceName);
+            result |= mDeviceBroker.handleDeviceConnection(isActive, audioDevice.getInternalType(),
+                    audioDevice.getAddress(), btDeviceName);
         } else {
+            int[] outDeviceTypes = {
+                    AudioSystem.DEVICE_OUT_BLUETOOTH_SCO,
+                    AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
+                    AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT
+            };
             for (int outDeviceType : outDeviceTypes) {
                 result |= mDeviceBroker.handleDeviceConnection(
-                        isActive, outDeviceType, address, btDeviceName);
+                        isActive, outDeviceType, audioDevice.getAddress(), btDeviceName);
             }
         }
         // handleDeviceConnection() && result to make sure the method get executed
         result = mDeviceBroker.handleDeviceConnection(
-                isActive, inDevice, address, btDeviceName) && result;
+                isActive, inDevice, audioDevice.getAddress(), btDeviceName) && result;
         return result;
     }
 
@@ -733,195 +676,122 @@
             };
 
     //----------------------------------------------------------------------
+
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    /*package*/ synchronized void scoClientDied(Object obj) {
-        final ScoClient client = (ScoClient) obj;
-        client.remove(true /*stop*/, false /*unregister*/);
-        Log.w(TAG, "SCO client died");
-    }
-
-    private class ScoClient implements IBinder.DeathRecipient {
-        private IBinder mCb; // To be notified of client's death
-        private int mCreatorPid;
-
-        ScoClient(IBinder cb) {
-            mCb = cb;
-            mCreatorPid = Binder.getCallingPid();
-        }
-
-        public void registerDeathRecipient() {
-            try {
-                mCb.linkToDeath(this, 0);
-            } catch (RemoteException e) {
-                Log.w(TAG, "ScoClient could not link to " + mCb + " binder death");
-            }
-        }
-
-        public void unregisterDeathRecipient() {
-            try {
-                mCb.unlinkToDeath(this, 0);
-            } catch (NoSuchElementException e) {
-                Log.w(TAG, "ScoClient could not not unregistered to binder");
-            }
-        }
-
-        @Override
-        public void binderDied() {
-            // process this from DeviceBroker's message queue to take the right locks since
-            // this event can impact SCO mode and requires querying audio mode stack
-            mDeviceBroker.postScoClientDied(this);
-        }
-
-        IBinder getBinder() {
-            return mCb;
-        }
-
-        int getPid() {
-            return mCreatorPid;
-        }
-
-        // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-        //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-        @GuardedBy("BtHelper.this")
-        private boolean requestScoState(int state, int scoAudioMode) {
-            checkScoAudioState();
-            if (mScoClients.size() != 1) {
-                Log.i(TAG, "requestScoState: state=" + state + ", scoAudioMode=" + scoAudioMode
-                        + ", num SCO clients=" + mScoClients.size());
-                return true;
-            }
-            if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
-                // Make sure that the state transitions to CONNECTING even if we cannot initiate
-                // the connection.
-                broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
-                // Accept SCO audio activation only in NORMAL audio mode or if the mode is
-                // currently controlled by the same client process.
-                final int modeOwnerPid =  mDeviceBroker.getModeOwnerPid();
-                if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) {
-                    Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid "
-                            + modeOwnerPid + " != creatorPid " + mCreatorPid);
+    //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+    @GuardedBy("BtHelper.this")
+    private boolean requestScoState(int state, int scoAudioMode) {
+        checkScoAudioState();
+        if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
+            // Make sure that the state transitions to CONNECTING even if we cannot initiate
+            // the connection.
+            broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
+            switch (mScoAudioState) {
+                case SCO_STATE_INACTIVE:
+                    mScoAudioMode = scoAudioMode;
+                    if (scoAudioMode == SCO_MODE_UNDEFINED) {
+                        mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
+                        if (mBluetoothHeadsetDevice != null) {
+                            mScoAudioMode = Settings.Global.getInt(
+                                    mDeviceBroker.getContentResolver(),
+                                    "bluetooth_sco_channel_"
+                                            + mBluetoothHeadsetDevice.getAddress(),
+                                    SCO_MODE_VIRTUAL_CALL);
+                            if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
+                                mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
+                            }
+                        }
+                    }
+                    if (mBluetoothHeadset == null) {
+                        if (getBluetoothHeadset()) {
+                            mScoAudioState = SCO_STATE_ACTIVATE_REQ;
+                        } else {
+                            Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
+                                    + " connection, mScoAudioMode=" + mScoAudioMode);
+                            broadcastScoConnectionState(
+                                    AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                            return false;
+                        }
+                        break;
+                    }
+                    if (mBluetoothHeadsetDevice == null) {
+                        Log.w(TAG, "requestScoState: no active device while connecting,"
+                                + " mScoAudioMode=" + mScoAudioMode);
+                        broadcastScoConnectionState(
+                                AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                        return false;
+                    }
+                    if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
+                            mBluetoothHeadsetDevice, mScoAudioMode)) {
+                        mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+                    } else {
+                        Log.w(TAG, "requestScoState: connect to "
+                                + mBluetoothHeadsetDevice
+                                + " failed, mScoAudioMode=" + mScoAudioMode);
+                        broadcastScoConnectionState(
+                                AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                        return false;
+                    }
+                    break;
+                case SCO_STATE_DEACTIVATING:
+                    mScoAudioState = SCO_STATE_ACTIVATE_REQ;
+                    break;
+                case SCO_STATE_DEACTIVATE_REQ:
+                    mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+                    broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
+                    break;
+                case SCO_STATE_ACTIVE_INTERNAL:
+                    Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return");
+                    break;
+                default:
+                    Log.w(TAG, "requestScoState: failed to connect in state "
+                            + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
                     broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
                     return false;
-                }
-                switch (mScoAudioState) {
-                    case SCO_STATE_INACTIVE:
-                        mScoAudioMode = scoAudioMode;
-                        if (scoAudioMode == SCO_MODE_UNDEFINED) {
-                            mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
-                            if (mBluetoothHeadsetDevice != null) {
-                                mScoAudioMode = Settings.Global.getInt(
-                                        mDeviceBroker.getContentResolver(),
-                                        "bluetooth_sco_channel_"
-                                                + mBluetoothHeadsetDevice.getAddress(),
-                                        SCO_MODE_VIRTUAL_CALL);
-                                if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
-                                    mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
-                                }
-                            }
-                        }
-                        if (mBluetoothHeadset == null) {
-                            if (getBluetoothHeadset()) {
-                                mScoAudioState = SCO_STATE_ACTIVATE_REQ;
-                            } else {
-                                Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
-                                        + " connection, mScoAudioMode=" + mScoAudioMode);
-                                broadcastScoConnectionState(
-                                        AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                                return false;
-                            }
-                            break;
-                        }
-                        if (mBluetoothHeadsetDevice == null) {
-                            Log.w(TAG, "requestScoState: no active device while connecting,"
-                                    + " mScoAudioMode=" + mScoAudioMode);
-                            broadcastScoConnectionState(
-                                    AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                            return false;
-                        }
-                        if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
-                                mBluetoothHeadsetDevice, mScoAudioMode)) {
-                            mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+            }
+        } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
+            switch (mScoAudioState) {
+                case SCO_STATE_ACTIVE_INTERNAL:
+                    if (mBluetoothHeadset == null) {
+                        if (getBluetoothHeadset()) {
+                            mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
                         } else {
-                            Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice
-                                    + " failed, mScoAudioMode=" + mScoAudioMode);
+                            Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
+                                    + " disconnection, mScoAudioMode=" + mScoAudioMode);
+                            mScoAudioState = SCO_STATE_INACTIVE;
                             broadcastScoConnectionState(
                                     AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
                             return false;
                         }
                         break;
-                    case SCO_STATE_DEACTIVATING:
-                        mScoAudioState = SCO_STATE_ACTIVATE_REQ;
-                        break;
-                    case SCO_STATE_DEACTIVATE_REQ:
-                        mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
-                        broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
-                        break;
-                    case SCO_STATE_ACTIVE_INTERNAL:
-                        Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return");
-                        break;
-                    default:
-                        Log.w(TAG, "requestScoState: failed to connect in state "
-                                + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
-                        broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                        return false;
-                }
-            } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
-                switch (mScoAudioState) {
-                    case SCO_STATE_ACTIVE_INTERNAL:
-                        if (mBluetoothHeadset == null) {
-                            if (getBluetoothHeadset()) {
-                                mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
-                            } else {
-                                Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
-                                        + " disconnection, mScoAudioMode=" + mScoAudioMode);
-                                mScoAudioState = SCO_STATE_INACTIVE;
-                                broadcastScoConnectionState(
-                                        AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                                return false;
-                            }
-                            break;
-                        }
-                        if (mBluetoothHeadsetDevice == null) {
-                            mScoAudioState = SCO_STATE_INACTIVE;
-                            broadcastScoConnectionState(
-                                    AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                            break;
-                        }
-                        if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
-                                mBluetoothHeadsetDevice, mScoAudioMode)) {
-                            mScoAudioState = SCO_STATE_DEACTIVATING;
-                        } else {
-                            mScoAudioState = SCO_STATE_INACTIVE;
-                            broadcastScoConnectionState(
-                                    AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                        }
-                        break;
-                    case SCO_STATE_ACTIVATE_REQ:
+                    }
+                    if (mBluetoothHeadsetDevice == null) {
                         mScoAudioState = SCO_STATE_INACTIVE;
-                        broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                        broadcastScoConnectionState(
+                                AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
                         break;
-                    default:
-                        Log.w(TAG, "requestScoState: failed to disconnect in state "
-                                + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
-                        broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                        return false;
-                }
+                    }
+                    if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
+                            mBluetoothHeadsetDevice, mScoAudioMode)) {
+                        mScoAudioState = SCO_STATE_DEACTIVATING;
+                    } else {
+                        mScoAudioState = SCO_STATE_INACTIVE;
+                        broadcastScoConnectionState(
+                                AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                    }
+                    break;
+                case SCO_STATE_ACTIVATE_REQ:
+                    mScoAudioState = SCO_STATE_INACTIVE;
+                    broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                    break;
+                default:
+                    Log.w(TAG, "requestScoState: failed to disconnect in state "
+                            + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
+                    broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                    return false;
             }
-            return true;
         }
-
-        @GuardedBy("BtHelper.this")
-        void remove(boolean stop, boolean unregister) {
-            if (unregister) {
-                unregisterDeathRecipient();
-            }
-            if (stop) {
-                requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
-                        SCO_MODE_VIRTUAL_CALL);
-            }
-            mScoClients.remove(this);
-        }
+        return true;
     }
 
     //-----------------------------------------------------
@@ -974,49 +844,6 @@
         }
     }
 
-
-    @GuardedBy("BtHelper.this")
-    private ScoClient getScoClient(IBinder cb, boolean create) {
-        for (ScoClient existingClient : mScoClients) {
-            if (existingClient.getBinder() == cb) {
-                return existingClient;
-            }
-        }
-        if (create) {
-            ScoClient newClient = new ScoClient(cb);
-            newClient.registerDeathRecipient();
-            mScoClients.add(newClient);
-            return newClient;
-        }
-        return null;
-    }
-
-    @GuardedBy("BtHelper.this")
-    private ScoClient getScoClientForPid(int pid) {
-        for (ScoClient cl : mScoClients) {
-            if (cl.getPid() == pid) {
-                return cl;
-            }
-        }
-        return null;
-    }
-
-    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-    //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    @GuardedBy("BtHelper.this")
-    private void clearAllScoClients(int exceptPid, boolean stopSco) {
-        final ArrayList<ScoClient> clients = new ArrayList<ScoClient>();
-        for (ScoClient cl : mScoClients) {
-            if (cl.getPid() != exceptPid) {
-                clients.add(cl);
-            }
-        }
-        for (ScoClient cl : clients) {
-            cl.remove(stopSco, true /*unregister*/);
-        }
-
-    }
-
     private boolean getBluetoothHeadset() {
         boolean result = false;
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -1062,10 +889,6 @@
         pw.println(prefix + "mBluetoothHeadsetDevice: " + mBluetoothHeadsetDevice);
         pw.println(prefix + "mScoAudioState: " + scoAudioStateToString(mScoAudioState));
         pw.println(prefix + "mScoAudioMode: " + scoAudioModeToString(mScoAudioMode));
-        pw.println(prefix + "Sco clients:");
-        mScoClients.forEach((cl) -> {
-            pw.println("  " + prefix + "pid: " + cl.getPid() + " cb: " + cl.getBinder()); });
-
         pw.println("\n" + prefix + "mHearingAid: " + mHearingAid);
         pw.println(prefix + "mA2dp: " + mA2dp);
         pw.println(prefix + "mAvrcpAbsVolSupported: " + mAvrcpAbsVolSupported);
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index ff31931..a8aa9aa 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -143,6 +143,9 @@
      * @return {@code true} if the change should be enabled for the package.
      */
     boolean isEnabled(ApplicationInfo app) {
+        if (app == null) {
+            return defaultValue();
+        }
         if (mPackageOverrides != null && mPackageOverrides.containsKey(app.packageName)) {
             return mPackageOverrides.get(app.packageName);
         }
@@ -156,6 +159,15 @@
     }
 
     /**
+     * Returns the default value for the change id, assuming there are no overrides.
+     *
+     * @return {@code false} if it's a default disabled change, {@code true} otherwise.
+     */
+    boolean defaultValue() {
+        return !getDisabled();
+    }
+
+    /**
      * Checks whether a change has an override for a package.
      * @param packageName name of the package
      * @return true if there is such override
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index d80c58b..8511118 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -392,6 +392,14 @@
         return alreadyKnown;
     }
 
+    boolean defaultChangeIdValue(long changeId) {
+        CompatChange c = mChanges.get(changeId);
+        if (c == null) {
+            return true;
+        }
+        return c.defaultValue();
+    }
+
     @VisibleForTesting
     void clearChanges() {
         synchronized (mChanges) {
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 2c19a2d..aa85f7f 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -60,7 +60,6 @@
     private final CompatConfig mCompatConfig;
 
     private static int sMinTargetSdk = Build.VERSION_CODES.Q;
-    private static int sMaxTargetSdk = Build.VERSION_CODES.R;
 
     public PlatformCompat(Context context) {
         mContext = context;
@@ -107,17 +106,25 @@
     }
 
     /**
-     * Internal version of the above method. Does not perform costly permission check.
+     * Internal version of the above method, without logging. Does not perform costly permission
+     * check.
+     * TODO(b/167551701): Remove this method and add 'loggability' as a changeid property.
+     */
+    public boolean isChangeEnabledInternalNoLogging(long changeId, ApplicationInfo appInfo) {
+        return mCompatConfig.isChangeEnabled(changeId, appInfo);
+    }
+
+    /**
+     * Internal version of {@link #isChangeEnabled(long, ApplicationInfo)}. Does not perform costly
+     * permission check.
      */
     public boolean isChangeEnabledInternal(long changeId, ApplicationInfo appInfo) {
-        if (mCompatConfig.isChangeEnabled(changeId, appInfo)) {
+        boolean enabled = isChangeEnabledInternalNoLogging(changeId, appInfo);
+        if (appInfo != null) {
             reportChange(changeId, appInfo.uid,
-                    ChangeReporter.STATE_ENABLED);
-            return true;
+                    enabled ? ChangeReporter.STATE_ENABLED : ChangeReporter.STATE_DISABLED);
         }
-        reportChange(changeId, appInfo.uid,
-                ChangeReporter.STATE_DISABLED);
-        return false;
+        return enabled;
     }
 
     @Override
@@ -125,9 +132,6 @@
             @UserIdInt int userId) {
         checkCompatChangeReadAndLogPermission();
         ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
-        if (appInfo == null) {
-            return true;
-        }
         return isChangeEnabled(changeId, appInfo);
     }
 
@@ -136,7 +140,7 @@
         checkCompatChangeReadAndLogPermission();
         String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
         if (packages == null || packages.length == 0) {
-            return true;
+            return mCompatConfig.defaultChangeIdValue(changeId);
         }
         boolean enabled = true;
         for (String packageName : packages) {
@@ -379,8 +383,7 @@
             return false;
         }
         if (change.getEnableSinceTargetSdk() > 0) {
-            if (change.getEnableSinceTargetSdk() < sMinTargetSdk
-                    || change.getEnableSinceTargetSdk() > sMaxTargetSdk) {
+            if (change.getEnableSinceTargetSdk() < sMinTargetSdk) {
                 return false;
             }
         }
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index 342a11b..c4beddd4 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -28,9 +28,9 @@
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.TrafficStats.UID_REMOVED;
-import static android.net.NetworkUtils.multiplySafeByRational;
 import static android.text.format.DateUtils.WEEK_IN_MILLIS;
 
+import static com.android.internal.net.NetworkUtilsInternal.multiplySafeByRational;
 import static com.android.server.net.NetworkStatsService.TAG;
 
 import android.net.NetworkIdentity;
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index f116a24..c6a55b4 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -323,9 +323,9 @@
      * Copies the CE apex data directory for the given {@code userId} to a backup location, for use
      * in case of rollback.
      *
-     * @return long inode for the snapshot directory if the snapshot was successful, or -1 if not
+     * @return boolean true if the snapshot was successful
      */
-    public abstract long snapshotCeData(int userId, int rollbackId, String apexPackageName);
+    public abstract boolean snapshotCeData(int userId, int rollbackId, String apexPackageName);
 
     /**
      * Restores the snapshot of the CE apex data directory for the given {@code userId}.
@@ -826,7 +826,7 @@
         }
 
         @Override
-        public long snapshotCeData(int userId, int rollbackId, String apexPackageName) {
+        public boolean snapshotCeData(int userId, int rollbackId, String apexPackageName) {
             String apexModuleName;
             synchronized (mLock) {
                 Preconditions.checkState(mPackageNameToApexModuleName != null,
@@ -835,13 +835,14 @@
             }
             if (apexModuleName == null) {
                 Slog.e(TAG, "Invalid apex package name: " + apexPackageName);
-                return -1;
+                return false;
             }
             try {
-                return waitForApexService().snapshotCeData(userId, rollbackId, apexModuleName);
+                waitForApexService().snapshotCeData(userId, rollbackId, apexModuleName);
+                return true;
             } catch (Exception e) {
                 Slog.e(TAG, e.getMessage(), e);
-                return -1;
+                return false;
             }
         }
 
@@ -1124,7 +1125,7 @@
         }
 
         @Override
-        public long snapshotCeData(int userId, int rollbackId, String apexPackageName) {
+        public boolean snapshotCeData(int userId, int rollbackId, String apexPackageName) {
             throw new UnsupportedOperationException();
         }
 
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 069a00f..f221802 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -290,7 +290,8 @@
 
         private void updateEnabledState(@NonNull AndroidPackage pkg) {
             // TODO(b/135203078): Do not use toAppInfo
-            final boolean enabled = mInjector.getCompatibility().isChangeEnabledInternal(
+            // TODO(b/167551701): Make changeId non-logging
+            final boolean enabled = mInjector.getCompatibility().isChangeEnabledInternalNoLogging(
                     PackageManager.FILTER_APPLICATION_QUERY, pkg.toAppInfoWithoutState());
             if (enabled) {
                 mDisabledPackages.remove(pkg.getPackageName());
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
index 990055e..5c01e43 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@@ -32,6 +32,8 @@
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
 import android.os.SystemProperties;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
@@ -49,10 +51,6 @@
 import java.io.FileWriter;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
 
 /**
  * The recovery system service is responsible for coordinating recovery related
@@ -84,9 +82,9 @@
     private final Context mContext;
 
     @GuardedBy("this")
-    private final Map<String, IntentSender> mCallerPendingRequest = new HashMap<>();
+    private final ArrayMap<String, IntentSender> mCallerPendingRequest = new ArrayMap<>();
     @GuardedBy("this")
-    private final Set<String> mCallerPreparedForReboot = new HashSet<>();
+    private final ArraySet<String> mCallerPreparedForReboot = new ArraySet<>();
 
     /**
      * Need to prepare for resume on reboot.
@@ -121,7 +119,7 @@
     @IntDef({ ROR_NEED_PREPARATION,
             ROR_SKIP_PREPARATION_AND_NOTIFY,
             ROR_SKIP_PREPARATION_NOT_NOTIFY })
-    @interface ResumeOnRebootActionsOnRequest {}
+    private @interface ResumeOnRebootActionsOnRequest {}
 
     /**
      * The action to perform upon resume on reboot clear request for a given client.
@@ -129,7 +127,7 @@
     @IntDef({ROR_NOT_REQUESTED,
             ROR_REQUESTED_NEED_CLEAR,
             ROR_REQUESTED_SKIP_CLEAR})
-    @interface ResumeOnRebootActionsOnClear{}
+    private @interface ResumeOnRebootActionsOnClear{}
 
     static class Injector {
         protected final Context mContext;
@@ -342,9 +340,8 @@
                 != PackageManager.PERMISSION_GRANTED
                 && mContext.checkCallingOrSelfPermission(android.Manifest.permission.REBOOT)
                         != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Caller or self must have "
-                    + android.Manifest.permission.RECOVERY + " or "
-                    + android.Manifest.permission.REBOOT + " for resume on reboot.");
+            throw new SecurityException("Caller must have " + android.Manifest.permission.RECOVERY
+                    + " or " + android.Manifest.permission.REBOOT + " for resume on reboot.");
         }
     }
 
@@ -414,10 +411,14 @@
             Slog.w(TAG, "onPreparedForReboot called when some clients have prepared.");
         }
 
+        if (mCallerPendingRequest.isEmpty()) {
+            Slog.w(TAG, "onPreparedForReboot called but no client has requested.");
+        }
+
         // Send intents to notify callers
-        for (Map.Entry<String, IntentSender> entry : mCallerPendingRequest.entrySet()) {
-            sendPreparedForRebootIntentIfNeeded(entry.getValue());
-            mCallerPreparedForReboot.add(entry.getKey());
+        for (int i = 0; i < mCallerPendingRequest.size(); i++) {
+            sendPreparedForRebootIntentIfNeeded(mCallerPendingRequest.valueAt(i));
+            mCallerPreparedForReboot.add(mCallerPendingRequest.keyAt(i));
         }
         mCallerPendingRequest.clear();
     }
@@ -475,9 +476,7 @@
         return needClear ? ROR_REQUESTED_NEED_CLEAR : ROR_REQUESTED_SKIP_CLEAR;
     }
 
-    @Override // Binder call
-    public boolean rebootWithLskf(String packageName, String reason, boolean slotSwitch) {
-        enforcePermissionForResumeOnReboot();
+    private boolean rebootWithLskfImpl(String packageName, String reason, boolean slotSwitch) {
         if (packageName == null) {
             Slog.w(TAG, "Missing packageName when rebooting with lskf.");
             return false;
@@ -498,11 +497,29 @@
         return true;
     }
 
+    @Override // Binder call for the legacy rebootWithLskf
+    public boolean rebootWithLskfAssumeSlotSwitch(String packageName, String reason) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
+        return rebootWithLskfImpl(packageName, reason, true);
+    }
+
     @Override // Binder call
-    public synchronized boolean isLskfCaptured(String packageName) {
+    public boolean rebootWithLskf(String packageName, String reason, boolean slotSwitch) {
         enforcePermissionForResumeOnReboot();
-        if (!mCallerPreparedForReboot.contains(packageName)) {
-            Slog.i(TAG, "Reboot requested before prepare completed for caller " + packageName);
+        return rebootWithLskfImpl(packageName, reason, slotSwitch);
+    }
+
+    @Override // Binder call
+    public boolean isLskfCaptured(String packageName) {
+        enforcePermissionForResumeOnReboot();
+        boolean captured;
+        synchronized (this) {
+            captured = mCallerPreparedForReboot.contains(packageName);
+        }
+
+        if (!captured) {
+            Slog.i(TAG, "Reboot requested before prepare completed for caller "
+                    + packageName);
             return false;
         }
         return true;
diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
index 524ae54..0213cc6 100644
--- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
+++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
@@ -129,13 +129,8 @@
         if (packageRollbackInfo.isApex()) {
             // For APEX, only snapshot CE here
             if ((flags & Installer.FLAG_STORAGE_CE) != 0) {
-                long ceSnapshotInode = mApexManager.snapshotCeData(
+                return mApexManager.snapshotCeData(
                         userId, rollbackId, packageRollbackInfo.getPackageName());
-                if (ceSnapshotInode > 0) {
-                    packageRollbackInfo.putCeSnapshotInode(userId, ceSnapshotInode);
-                } else {
-                    return false;
-                }
             }
         } else {
             // APK
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index faf3f06..b20c493 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -195,7 +195,6 @@
 import android.media.IAudioService;
 import android.net.ConnectivityManager;
 import android.net.IIpConnectivityMetrics;
-import android.net.NetworkUtils;
 import android.net.ProxyInfo;
 import android.net.Uri;
 import android.net.metrics.IpConnectivityLog;
@@ -267,6 +266,7 @@
 import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.net.NetworkUtilsInternal;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.statusbar.IStatusBarService;
@@ -15540,7 +15540,7 @@
                 return PRIVATE_DNS_SET_NO_ERROR;
             case PRIVATE_DNS_MODE_PROVIDER_HOSTNAME:
                 if (TextUtils.isEmpty(privateDnsHost)
-                        || !NetworkUtils.isWeaklyValidatedHostname(privateDnsHost)) {
+                        || !NetworkUtilsInternal.isWeaklyValidatedHostname(privateDnsHost)) {
                     throw new IllegalArgumentException(
                             String.format("Provided hostname %s is not valid", privateDnsHost));
                 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS b/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS
new file mode 100644
index 0000000..d825dfd
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/pm/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
index a9cef20..8d706cb 100644
--- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
+++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
@@ -21,6 +21,8 @@
 import android.media.AudioSystem;
 import android.util.Log;
 
+import java.util.List;
+
 /**
  * Provides an adapter for AudioSystem that does nothing.
  * Overridden methods can be configured.
@@ -66,13 +68,30 @@
     }
 
     @Override
-    public int setPreferredDeviceForStrategy(int strategy,
-            @NonNull AudioDeviceAttributes device) {
+    public int setDevicesRoleForStrategy(int strategy, int role,
+            @NonNull List<AudioDeviceAttributes> devices) {
         return AudioSystem.AUDIO_STATUS_OK;
     }
 
     @Override
-    public int removePreferredDeviceForStrategy(int strategy) {
+    public int removeDevicesRoleForStrategy(int strategy, int role) {
+        return AudioSystem.AUDIO_STATUS_OK;
+    }
+
+    @Override
+    public int setDevicesRoleForCapturePreset(int capturePreset, int role,
+                                              @NonNull List<AudioDeviceAttributes> devices) {
+        return AudioSystem.AUDIO_STATUS_OK;
+    }
+
+    @Override
+    public int removeDevicesRoleForCapturePreset(
+            int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devicesToRemove) {
+        return AudioSystem.AUDIO_STATUS_OK;
+    }
+
+    @Override
+    public int clearDevicesRoleForCapturePreset(int capturePreset, int role) {
         return AudioSystem.AUDIO_STATUS_OK;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/audio/OWNERS b/services/tests/servicestests/src/com/android/server/audio/OWNERS
new file mode 100644
index 0000000..894a1f5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/audio/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index e588370..8c63bfc 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -190,6 +190,22 @@
     }
 
     @Test
+    public void testIsChangeEnabledForInvalidApp() throws Exception {
+        final long disabledChangeId = 1234L;
+        final long enabledChangeId = 1235L;
+        final long targetSdkChangeId = 1236L;
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addEnabledChangeWithId(enabledChangeId)
+                .addDisabledChangeWithId(disabledChangeId)
+                .addEnableSinceSdkChangeWithId(42, targetSdkChangeId)
+                .build();
+
+        assertThat(compatConfig.isChangeEnabled(enabledChangeId, null)).isTrue();
+        assertThat(compatConfig.isChangeEnabled(disabledChangeId, null)).isFalse();
+        assertThat(compatConfig.isChangeEnabled(targetSdkChangeId, null)).isTrue();
+    }
+
+    @Test
     public void testPreventAddOverride() throws Exception {
         final long changeId = 1234L;
         CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 64014ba..1d3b643 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -107,18 +107,20 @@
         mCompatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
                 .addEnabledChangeWithId(1L)
                 .addDisabledChangeWithIdAndName(2L, "change2")
-                .addEnableAfterSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "desc")
-                .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.P, 4L)
-                .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
-                .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.R, 6L)
+                .addEnableSinceSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "desc")
+                .addEnableSinceSdkChangeWithId(Build.VERSION_CODES.P, 4L)
+                .addEnableSinceSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
+                .addEnableSinceSdkChangeWithId(Build.VERSION_CODES.R, 6L)
                 .addLoggingOnlyChangeWithId(7L)
                 .build();
         mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
         assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly(
                 new CompatibilityChangeInfo(1L, "", -1, -1, false, false, ""),
                 new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, ""),
-                new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, -1, false, false, ""),
-                new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, -1, false, false, ""));
+                new CompatibilityChangeInfo(5L, "", /*enableAfter*/ -1,
+                        /*enableSince*/ Build.VERSION_CODES.Q, false, false, ""),
+                new CompatibilityChangeInfo(6L, "", /*enableAfter*/ -1,
+                        /*enableSince*/ Build.VERSION_CODES.R, false, false, ""));
     }
 
     @Test
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 11bb867..4e9e6a8 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2046,7 +2046,13 @@
      * via {@link android.telecom.PhoneAccount#CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE}
      * and can choose to hide or show the video calling icon based on whether a contact supports
      * video.
+     *
+     * @deprecated No longer used in framework code, however it may still be used by applications
+     * that have not updated their code. This config should still be set to {@code true} if
+     * {@link Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is set to {@code true} and
+     * {@link Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL} is set to {@code true}.
      */
+    @Deprecated
     public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
 
     /**
@@ -3862,13 +3868,51 @@
          * <p>
          * If this key's value is set to false, the procedure for RCS contact capability exchange
          * via SIP SUBSCRIBE/NOTIFY will also be disabled internally, and
-         * {@link #KEY_USE_RCS_PRESENCE_BOOL} must also be set to false to ensure apps do not
-         * improperly think that capability exchange via SIP PUBLISH is enabled.
+         * {@link Ims#KEY_ENABLE_PRESENCE_PUBLISH_BOOL} must also be set to false to ensure
+         * apps do not improperly think that capability exchange via SIP PUBLISH is enabled.
          * <p> The default value for this key is {@code false}.
          */
         public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL =
                 KEY_PREFIX + "enable_presence_publish_bool";
 
+        /**
+         * Flag indicating whether or not this carrier supports the exchange of phone numbers with
+         * the carrier's RCS presence server in order to retrieve the RCS capabilities of requested
+         * contacts used in the RCS User Capability Exchange (UCE) procedure. See RCC.71, section 3
+         * for more information.
+         * <p>
+         * When presence is supported, the device uses the SIP SUBSCRIBE/NOTIFY procedure internally
+         * to retrieve the requested RCS capabilities. See
+         * {@link android.telephony.ims.RcsUceAdapter} for more information on how RCS capabilities
+         * can be retrieved from the carrier's network.
+         */
+        public static final String KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL =
+                KEY_PREFIX + "enable_presence_capability_exchange_bool";
+
+
+        /**
+         * Flag indicating whether or not the carrier expects the RCS UCE service to periodically
+         * refresh the RCS capabilities cache of the user's contacts as well as request the
+         * capabilities of call contacts when the SIM card is first inserted or when a new contact
+         * is added, removed, or modified. This corresponds to the RCC.07 A.19
+         * "DISABLE INITIAL ADDRESS BOOK SCAN" parameter.
+         * <p>
+         * If this flag is disabled, the capabilities cache will not be refreshed internally at all
+         * and will only be updated if the cached capabilities are stale when an application
+         * requests them.
+         */
+        public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL =
+                KEY_PREFIX + "rcs_bulk_capability_exchange_bool";
+
+        /**
+         * Flag indicating whether or not the carrier supports capability exchange with a list of
+         * contacts. When {@code true}, the device will batch together multiple requests and
+         * construct a RLMI document in the SIP SUBSCRIBE request (see RFC 4662). If {@code false},
+         * the request will be split up into one SIP SUBSCRIBE request per contact.
+         */
+        public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL =
+                KEY_PREFIX + "enable_presence_group_subscribe_bool";
+
         private Ims() {}
 
         private static PersistableBundle getDefaults() {
@@ -3876,6 +3920,9 @@
             defaults.putInt(KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT, 4000);
             defaults.putBoolean(KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false);
             defaults.putBoolean(KEY_ENABLE_PRESENCE_PUBLISH_BOOL, false);
+            defaults.putBoolean(KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL, false);
+            defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false);
+            defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true);
             return defaults;
         }
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 26fd99d..0e06444 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -9338,10 +9338,9 @@
      * @return true if mobile data is enabled.
      */
     @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-            android.Manifest.permission.READ_PHONE_STATE})
+            android.Manifest.permission.MODIFY_PHONE_STATE})
     public boolean isDataEnabled() {
-        return isDataEnabledForReason(DATA_ENABLED_REASON_USER);
+        return getDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
     }
 
     /**
@@ -9586,7 +9585,7 @@
     @SystemApi
     public boolean getDataEnabled(int subId) {
         try {
-            return isDataEnabledForReason(subId, DATA_ENABLED_REASON_USER);
+            return isDataEnabledForReason(DATA_ENABLED_REASON_USER);
         } catch (RuntimeException e) {
             Log.e(TAG, "Error calling isDataEnabledForReason e:" + e);
         }
@@ -12301,23 +12300,15 @@
     @NonNull
     public Map<Integer, List<EmergencyNumber>> getEmergencyNumberList(
             @EmergencyServiceCategories int categories) {
-        Map<Integer, List<EmergencyNumber>> emergencyNumberList = new HashMap<>();
+        Map<Integer, List<EmergencyNumber>> emergencyNumberListForCategories = new HashMap<>();
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                emergencyNumberList = telephony.getEmergencyNumberList(
-                        mContext.getOpPackageName(), mContext.getAttributionTag());
-                if (emergencyNumberList != null) {
-                    for (Integer subscriptionId : emergencyNumberList.keySet()) {
-                        List<EmergencyNumber> numberList = emergencyNumberList.get(subscriptionId);
-                        for (EmergencyNumber number : numberList) {
-                            if (!number.isInEmergencyServiceCategories(categories)) {
-                                numberList.remove(number);
-                            }
-                        }
-                    }
-                }
-                return emergencyNumberList;
+                Map<Integer, List<EmergencyNumber>> emergencyNumberList =
+                        telephony.getEmergencyNumberList(mContext.getOpPackageName(),
+                                mContext.getAttributionTag());
+                emergencyNumberListForCategories =
+                        filterEmergencyNumbersByCategories(emergencyNumberList, categories);
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
@@ -12325,7 +12316,34 @@
             Log.e(TAG, "getEmergencyNumberList with Categories RemoteException", ex);
             ex.rethrowAsRuntimeException();
         }
-        return emergencyNumberList;
+        return emergencyNumberListForCategories;
+    }
+
+    /**
+     * Filter emergency numbers with categories.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public Map<Integer, List<EmergencyNumber>> filterEmergencyNumbersByCategories(
+            Map<Integer, List<EmergencyNumber>> emergencyNumberList,
+                    @EmergencyServiceCategories int categories) {
+        Map<Integer, List<EmergencyNumber>> emergencyNumberListForCategories = new HashMap<>();
+        if (emergencyNumberList != null) {
+            for (Integer subscriptionId : emergencyNumberList.keySet()) {
+                List<EmergencyNumber> allNumbersForSub = emergencyNumberList.get(
+                        subscriptionId);
+                List<EmergencyNumber> numbersForCategoriesPerSub = new ArrayList<>();
+                for (EmergencyNumber number : allNumbersForSub) {
+                    if (number.isInEmergencyServiceCategories(categories)) {
+                        numbersForCategoriesPerSub.add(number);
+                    }
+                }
+                emergencyNumberListForCategories.put(
+                        subscriptionId, numbersForCategoriesPerSub);
+            }
+        }
+        return emergencyNumberListForCategories;
     }
 
     /**
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 0d8e5bc..bd623e0 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -30,7 +30,6 @@
 import android.provider.Settings;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.BinderCacheManager;
-import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsRcsController;
@@ -62,9 +61,10 @@
      * been enabled by the user can be queried using {@link RcsUceAdapter#isUceSettingEnabled()}.
      * <p>
      * This intent will always be handled by the system, however the application should only send
-     * this Intent if the carrier supports RCS contact discovery, which can be queried using the key
-     * {@link CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL}. Otherwise, the RCS contact discovery
-     * opt-in dialog will not be shown.
+     * this Intent if the carrier supports bulk RCS contact exchange, which will be true if either
+     * key {@link android.telephony.CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL}
+     * or {@link android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL} is set to true.
+     * Otherwise, the RCS contact discovery opt-in dialog will not be shown.
      * <p>
      * Input: A mandatory {@link Settings#EXTRA_SUB_ID} extra containing the subscription that the
      * setting will be be shown for.
@@ -396,6 +396,7 @@
      * rather the subscription is capable of this service over IMS.
      * @see #isAvailable(int)
      * @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL
+     * @see android.telephony.CarrierConfigManager.Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL
      * @throws ImsException if the IMS service is not available when calling this method.
      * See {@link ImsException#getCode()} for more information on the error codes.
      * @hide
diff --git a/tests/net/common/java/android/net/NetworkProviderTest.kt b/tests/net/common/java/android/net/NetworkProviderTest.kt
index 77e9f12..bcc9072 100644
--- a/tests/net/common/java/android/net/NetworkProviderTest.kt
+++ b/tests/net/common/java/android/net/NetworkProviderTest.kt
@@ -29,6 +29,7 @@
 import com.android.net.module.util.ArrayTrackRecord
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
 import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.isDevSdkInRange
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
@@ -173,10 +174,12 @@
     @Test
     fun testDeclareNetworkRequestUnfulfillable() {
         val mockContext = mock(Context::class.java)
-        val provider = createNetworkProvider(mockContext)
-        // ConnectivityManager not required at creation time
-        verifyNoMoreInteractions(mockContext)
         doReturn(mCm).`when`(mockContext).getSystemService(Context.CONNECTIVITY_SERVICE)
+        val provider = createNetworkProvider(mockContext)
+        // ConnectivityManager not required at creation time after R
+        if (!isDevSdkInRange(0, Build.VERSION_CODES.R)) {
+            verifyNoMoreInteractions(mockContext)
+        }
 
         mCm.registerNetworkProvider(provider)
 
diff --git a/tests/net/java/android/net/NetworkUtilsTest.java b/tests/net/java/android/net/NetworkUtilsTest.java
index 3158cc8..7748288 100644
--- a/tests/net/java/android/net/NetworkUtilsTest.java
+++ b/tests/net/java/android/net/NetworkUtilsTest.java
@@ -16,24 +16,10 @@
 
 package android.net;
 
-import static android.system.OsConstants.AF_INET;
-import static android.system.OsConstants.AF_INET6;
-import static android.system.OsConstants.AF_UNIX;
-import static android.system.OsConstants.EPERM;
-import static android.system.OsConstants.SOCK_DGRAM;
-import static android.system.OsConstants.SOCK_STREAM;
-
 import static junit.framework.Assert.assertEquals;
 
-import static org.junit.Assert.fail;
-
-import android.system.ErrnoException;
-import android.system.Os;
-
 import androidx.test.runner.AndroidJUnit4;
 
-import libcore.io.IoUtils;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -139,50 +125,4 @@
         assertEquals(BigInteger.valueOf(7l - 4 + 4 + 16 + 65536),
                 NetworkUtils.routedIPv6AddressCount(set));
     }
-
-    private static void expectSocketSuccess(String msg, int domain, int type) {
-        try {
-            IoUtils.closeQuietly(Os.socket(domain, type, 0));
-        } catch (ErrnoException e) {
-            fail(msg + e.getMessage());
-        }
-    }
-
-    private static void expectSocketPemissionError(String msg, int domain, int type) {
-        try {
-            IoUtils.closeQuietly(Os.socket(domain, type, 0));
-            fail(msg);
-        } catch (ErrnoException e) {
-            assertEquals(msg, e.errno, EPERM);
-        }
-    }
-
-    private static void expectHasNetworking() {
-        expectSocketSuccess("Creating a UNIX socket should not have thrown ErrnoException",
-                AF_UNIX, SOCK_STREAM);
-        expectSocketSuccess("Creating a AF_INET socket shouldn't have thrown ErrnoException",
-                AF_INET, SOCK_DGRAM);
-        expectSocketSuccess("Creating a AF_INET6 socket shouldn't have thrown ErrnoException",
-                AF_INET6, SOCK_DGRAM);
-    }
-
-    private static void expectNoNetworking() {
-        expectSocketSuccess("Creating a UNIX socket should not have thrown ErrnoException",
-                AF_UNIX, SOCK_STREAM);
-        expectSocketPemissionError(
-                "Creating a AF_INET socket should have thrown ErrnoException(EPERM)",
-                AF_INET, SOCK_DGRAM);
-        expectSocketPemissionError(
-                "Creating a AF_INET6 socket should have thrown ErrnoException(EPERM)",
-                AF_INET6, SOCK_DGRAM);
-    }
-
-    @Test
-    public void testSetAllowNetworkingForProcess() {
-        expectHasNetworking();
-        NetworkUtils.setAllowNetworkingForProcess(false);
-        expectNoNetworking();
-        NetworkUtils.setAllowNetworkingForProcess(true);
-        expectHasNetworking();
-    }
 }
diff --git a/tests/net/java/com/android/internal/net/NetworkUtilsInternalTest.java b/tests/net/java/com/android/internal/net/NetworkUtilsInternalTest.java
new file mode 100644
index 0000000..3cfecd5
--- /dev/null
+++ b/tests/net/java/com/android/internal/net/NetworkUtilsInternalTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.net;
+
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.AF_UNIX;
+import static android.system.OsConstants.EPERM;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOCK_STREAM;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.junit.Assert.fail;
+
+import android.system.ErrnoException;
+import android.system.Os;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import libcore.io.IoUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@androidx.test.filters.SmallTest
+public class NetworkUtilsInternalTest {
+
+    private static void expectSocketSuccess(String msg, int domain, int type) {
+        try {
+            IoUtils.closeQuietly(Os.socket(domain, type, 0));
+        } catch (ErrnoException e) {
+            fail(msg + e.getMessage());
+        }
+    }
+
+    private static void expectSocketPemissionError(String msg, int domain, int type) {
+        try {
+            IoUtils.closeQuietly(Os.socket(domain, type, 0));
+            fail(msg);
+        } catch (ErrnoException e) {
+            assertEquals(msg, e.errno, EPERM);
+        }
+    }
+
+    private static void expectHasNetworking() {
+        expectSocketSuccess("Creating a UNIX socket should not have thrown ErrnoException",
+                AF_UNIX, SOCK_STREAM);
+        expectSocketSuccess("Creating a AF_INET socket shouldn't have thrown ErrnoException",
+                AF_INET, SOCK_DGRAM);
+        expectSocketSuccess("Creating a AF_INET6 socket shouldn't have thrown ErrnoException",
+                AF_INET6, SOCK_DGRAM);
+    }
+
+    private static void expectNoNetworking() {
+        expectSocketSuccess("Creating a UNIX socket should not have thrown ErrnoException",
+                AF_UNIX, SOCK_STREAM);
+        expectSocketPemissionError(
+                "Creating a AF_INET socket should have thrown ErrnoException(EPERM)",
+                AF_INET, SOCK_DGRAM);
+        expectSocketPemissionError(
+                "Creating a AF_INET6 socket should have thrown ErrnoException(EPERM)",
+                AF_INET6, SOCK_DGRAM);
+    }
+
+    @Test
+    public void testSetAllowNetworkingForProcess() {
+        expectHasNetworking();
+        NetworkUtilsInternal.setAllowNetworkingForProcess(false);
+        expectNoNetworking();
+        NetworkUtilsInternal.setAllowNetworkingForProcess(true);
+        expectHasNetworking();
+    }
+}
diff --git a/tests/net/java/com/android/server/NetworkManagementServiceTest.java b/tests/net/java/com/android/server/NetworkManagementServiceTest.java
index ea763d2..e150229 100644
--- a/tests/net/java/com/android/server/NetworkManagementServiceTest.java
+++ b/tests/net/java/com/android/server/NetworkManagementServiceTest.java
@@ -68,11 +68,12 @@
 @SmallTest
 public class NetworkManagementServiceTest {
     private NetworkManagementService mNMService;
-
     @Mock private Context mContext;
     @Mock private IBatteryStats.Stub mBatteryStatsService;
     @Mock private INetd.Stub mNetdService;
 
+    private static final int TEST_UID = 111;
+
     @NonNull
     @Captor
     private ArgumentCaptor<INetdUnsolicitedEventListener> mUnsolListenerCaptor;
@@ -165,14 +166,14 @@
         /**
          * Interface class activity.
          */
-        unsolListener.onInterfaceClassActivityChanged(true, 1, 1234, 0);
-        expectSoon(observer).interfaceClassDataActivityChanged("1", true, 1234);
+        unsolListener.onInterfaceClassActivityChanged(true, 1, 1234, TEST_UID);
+        expectSoon(observer).interfaceClassDataActivityChanged("1", true, 1234, TEST_UID);
 
-        unsolListener.onInterfaceClassActivityChanged(false, 9, 5678, 0);
-        expectSoon(observer).interfaceClassDataActivityChanged("9", false, 5678);
+        unsolListener.onInterfaceClassActivityChanged(false, 9, 5678, TEST_UID);
+        expectSoon(observer).interfaceClassDataActivityChanged("9", false, 5678, TEST_UID);
 
-        unsolListener.onInterfaceClassActivityChanged(false, 9, 4321, 0);
-        expectSoon(observer).interfaceClassDataActivityChanged("9", false, 4321);
+        unsolListener.onInterfaceClassActivityChanged(false, 9, 4321, TEST_UID);
+        expectSoon(observer).interfaceClassDataActivityChanged("9", false, 4321, TEST_UID);
 
         /**
          * IP address changes.
@@ -222,8 +223,6 @@
         assertFalse(mNMService.isFirewallEnabled());
     }
 
-    private static final int TEST_UID = 111;
-
     @Test
     public void testNetworkRestrictedDefault() {
         assertFalse(mNMService.isNetworkRestricted(TEST_UID));
diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
index fb0cfc0..89146f9 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
@@ -23,11 +23,11 @@
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.NetworkStatsHistory.FIELD_ALL;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
-import static android.net.NetworkUtils.multiplySafeByRational;
 import static android.os.Process.myUid;
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 
+import static com.android.internal.net.NetworkUtilsInternal.multiplySafeByRational;
 import static com.android.testutils.MiscAsserts.assertThrows;
 
 import static org.junit.Assert.assertArrayEquals;
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index ef33c34..793a236 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -531,14 +531,14 @@
 }
 
 StringPiece16 GetString16(const android::ResStringPool& pool, size_t idx) {
-  if (auto str = pool.stringAt(idx)) {
+  if (auto str = pool.stringAt(idx); str.ok()) {
     return *str;
   }
   return StringPiece16();
 }
 
 std::string GetString(const android::ResStringPool& pool, size_t idx) {
-  if (auto str = pool.string8At(idx)) {
+  if (auto str = pool.string8At(idx); str.ok()) {
     return ModifiedUtf8ToUtf8(str->to_string());
   }
   return Utf16ToUtf8(GetString16(pool, idx));
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 8b89959..49138ab 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -31,9 +31,6 @@
         "java/**/*.java",
         "java/**/*.aidl",
     ],
-    exclude_srcs: [
-        ":framework-wifi-non-updatable-sources"
-    ],
     path: "java",
     visibility: ["//visibility:private"],
 }
@@ -47,20 +44,6 @@
 }
 
 filegroup {
-    name: "framework-wifi-non-updatable-sources",
-    srcs: [
-        // TODO(b/146011398) package android.net.wifi is now split amongst 2 jars: framework.jar and
-        // framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache
-        // to a separate package.
-        "java/android/net/wifi/SoftApConfToXmlMigrationUtil.java",
-        "java/android/net/wifi/WifiNetworkScoreCache.java",
-        "java/android/net/wifi/WifiMigration.java",
-        "java/android/net/wifi/nl80211/*.java",
-        ":libwificond_ipc_aidl",
-    ],
-}
-
-filegroup {
     name: "framework-wifi-annotations",
     srcs: ["java/android/net/wifi/WifiAnnotations.java"],
 }
@@ -68,8 +51,12 @@
 // list of tests that are allowed to access @hide APIs from framework-wifi
 test_access_hidden_api_whitelist = [
     "//frameworks/base/wifi/tests",
+    "//frameworks/base/wifi/non-updatable/tests",
     "//frameworks/opt/net/wifi/tests/wifitests:__subpackages__",
 
+    "//packages/modules/Wifi/framework/tests",
+    "//packages/modules/Wifi/service/tests/wifitests:__subpackages__",
+
     "//external/robolectric-shadows:__subpackages__",
     "//frameworks/base/packages/SettingsLib/tests/integ",
     "//external/sl4a:__subpackages__",
diff --git a/wifi/non-updatable/java/Android.bp b/wifi/non-updatable/java/Android.bp
new file mode 100644
index 0000000..b35b4be
--- /dev/null
+++ b/wifi/non-updatable/java/Android.bp
@@ -0,0 +1,35 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This directory contains framework Wifi APIs that are not part of the Wifi module (i.e. not
+// updatable), and are generally only called by the Wifi module.
+
+// necessary since we only want the `path` property to only refer to these files
+filegroup {
+    name: "framework-wifi-non-updatable-sources-internal",
+    srcs: ["src/**/*.java"],
+    path: "src",
+    visibility: ["//visibility:private"],
+}
+
+filegroup {
+    name: "framework-wifi-non-updatable-sources",
+    srcs: [
+        // TODO(b/146011398) package android.net.wifi is now split amongst 2 jars: framework.jar and
+        // framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache
+        // to a separate package.
+        ":framework-wifi-non-updatable-sources-internal",
+        ":libwificond_ipc_aidl",
+    ],
+}
diff --git a/wifi/java/android/net/wifi/SoftApConfToXmlMigrationUtil.java b/wifi/non-updatable/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java
similarity index 100%
rename from wifi/java/android/net/wifi/SoftApConfToXmlMigrationUtil.java
rename to wifi/non-updatable/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java
diff --git a/wifi/java/android/net/wifi/WifiMigration.java b/wifi/non-updatable/java/src/android/net/wifi/WifiMigration.java
similarity index 98%
rename from wifi/java/android/net/wifi/WifiMigration.java
rename to wifi/non-updatable/java/src/android/net/wifi/WifiMigration.java
index 5792d27..4fabc0b 100755
--- a/wifi/java/android/net/wifi/WifiMigration.java
+++ b/wifi/non-updatable/java/src/android/net/wifi/WifiMigration.java
@@ -139,8 +139,8 @@
     /**
      * Load data from legacy shared wifi config store file.
      * <p>
-     * Expected AOSP format is available in the sample files under {@code /frameworks/base/wifi/
-     * java/android/net/wifi/migration_samples}.
+     * Expected AOSP format is available in the sample files under {@code
+     * frameworks/base/wifi/non-updatable/migration_samples/}.
      * </p>
      * <p>
      * Note:
@@ -218,8 +218,8 @@
     /**
      * Load data from legacy user wifi config store file.
      * <p>
-     * Expected AOSP format is available in the sample files under {@code /frameworks/base/wifi/
-     * java/android/net/wifi/migration_samples}.
+     * Expected AOSP format is available in the sample files under {@code
+     * frameworks/base/wifi/non-updatable/migration_samples/}.
      * </p>
      * <p>
      * Note:
diff --git a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java b/wifi/non-updatable/java/src/android/net/wifi/WifiNetworkScoreCache.java
similarity index 98%
rename from wifi/java/android/net/wifi/WifiNetworkScoreCache.java
rename to wifi/non-updatable/java/src/android/net/wifi/WifiNetworkScoreCache.java
index 378549d..3903658 100755
--- a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
+++ b/wifi/non-updatable/java/src/android/net/wifi/WifiNetworkScoreCache.java
@@ -89,7 +89,7 @@
 
     @Override public final void updateScores(List<ScoredNetwork> networks) {
         if (networks == null || networks.isEmpty()) {
-           return;
+            return;
         }
         if (DBG) {
             Log.d(TAG, "updateScores list size=" + networks.size());
@@ -97,7 +97,7 @@
 
         boolean changed = false;
 
-        synchronized(mLock) {
+        synchronized (mLock) {
             for (ScoredNetwork network : networks) {
                 String networkKey = buildNetworkKey(network);
                 if (networkKey == null) {
@@ -189,7 +189,7 @@
         String key = buildNetworkKey(result);
         if (key == null) return null;
 
-        synchronized(mLock) {
+        synchronized (mLock) {
             ScoredNetwork network = mCache.get(key);
             return network;
         }
diff --git a/wifi/java/android/net/wifi/nl80211/ChannelSettings.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/ChannelSettings.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/ChannelSettings.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/ChannelSettings.java
diff --git a/wifi/java/android/net/wifi/nl80211/DeviceWiphyCapabilities.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/DeviceWiphyCapabilities.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/DeviceWiphyCapabilities.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/DeviceWiphyCapabilities.java
diff --git a/wifi/java/android/net/wifi/nl80211/HiddenNetwork.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/HiddenNetwork.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/HiddenNetwork.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/HiddenNetwork.java
diff --git a/wifi/java/android/net/wifi/nl80211/NativeScanResult.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/NativeScanResult.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/NativeScanResult.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/NativeScanResult.java
diff --git a/wifi/java/android/net/wifi/nl80211/NativeWifiClient.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/NativeWifiClient.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/NativeWifiClient.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/NativeWifiClient.java
diff --git a/wifi/java/android/net/wifi/nl80211/PnoNetwork.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/PnoNetwork.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/PnoNetwork.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/PnoNetwork.java
diff --git a/wifi/java/android/net/wifi/nl80211/PnoSettings.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/PnoSettings.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/PnoSettings.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/PnoSettings.java
diff --git a/wifi/java/android/net/wifi/nl80211/RadioChainInfo.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/RadioChainInfo.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/RadioChainInfo.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/RadioChainInfo.java
diff --git a/wifi/java/android/net/wifi/nl80211/SingleScanSettings.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/SingleScanSettings.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/SingleScanSettings.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/SingleScanSettings.java
diff --git a/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
diff --git a/wifi/java/android/net/wifi/migration_samples/README.txt b/wifi/non-updatable/migration_samples/README.txt
similarity index 100%
rename from wifi/java/android/net/wifi/migration_samples/README.txt
rename to wifi/non-updatable/migration_samples/README.txt
diff --git a/wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStore.xml b/wifi/non-updatable/migration_samples/Shared_WifiConfigStore.xml
similarity index 100%
rename from wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStore.xml
rename to wifi/non-updatable/migration_samples/Shared_WifiConfigStore.xml
diff --git a/wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStoreSoftAp.xml b/wifi/non-updatable/migration_samples/Shared_WifiConfigStoreSoftAp.xml
similarity index 100%
rename from wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStoreSoftAp.xml
rename to wifi/non-updatable/migration_samples/Shared_WifiConfigStoreSoftAp.xml
diff --git a/wifi/java/android/net/wifi/migration_samples/User_WifiConfigStore.xml b/wifi/non-updatable/migration_samples/User_WifiConfigStore.xml
similarity index 100%
rename from wifi/java/android/net/wifi/migration_samples/User_WifiConfigStore.xml
rename to wifi/non-updatable/migration_samples/User_WifiConfigStore.xml
diff --git a/wifi/java/android/net/wifi/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml b/wifi/non-updatable/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml
similarity index 100%
rename from wifi/java/android/net/wifi/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml
rename to wifi/non-updatable/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml
diff --git a/wifi/non-updatable/tests/Android.bp b/wifi/non-updatable/tests/Android.bp
new file mode 100644
index 0000000..3f5cacf
--- /dev/null
+++ b/wifi/non-updatable/tests/Android.bp
@@ -0,0 +1,44 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+    name: "FrameworksWifiNonUpdatableApiTests",
+
+    defaults: ["framework-wifi-test-defaults"],
+
+    srcs: ["src/**/*.java"],
+
+    jacoco: {
+        include_filter: ["android.net.wifi.*"],
+        // TODO(b/147521214) need to exclude test classes
+        exclude_filter: [],
+    },
+
+    static_libs: [
+        "androidx.test.rules",
+        "frameworks-base-testutils",
+        "guava",
+        "mockito-target-minus-junit4",
+        "truth-prebuilt",
+    ],
+
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+
+    test_suites: [
+        "general-tests",
+    ],
+}
diff --git a/wifi/non-updatable/tests/AndroidManifest.xml b/wifi/non-updatable/tests/AndroidManifest.xml
new file mode 100644
index 0000000..b4b6b2d
--- /dev/null
+++ b/wifi/non-updatable/tests/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+     package="android.net.wifi.test">
+
+    <application>
+        <uses-library android:name="android.test.runner"/>
+        <activity android:label="WifiTestDummyLabel"
+             android:name="WifiTestDummyName"
+             android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+         android:targetPackage="android.net.wifi.test"
+         android:label="Frameworks Wifi Non-updatable API Tests">
+    </instrumentation>
+
+</manifest>
diff --git a/wifi/non-updatable/tests/AndroidTest.xml b/wifi/non-updatable/tests/AndroidTest.xml
new file mode 100644
index 0000000..5f3fdd4
--- /dev/null
+++ b/wifi/non-updatable/tests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs Frameworks Wifi Non-updatable API Tests.">
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="test-file-name" value="FrameworksWifiNonUpdatableApiTests.apk" />
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-tag" value="FrameworksWifiNonUpdatableApiTests" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.net.wifi.test" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/wifi/non-updatable/tests/README.md b/wifi/non-updatable/tests/README.md
new file mode 100644
index 0000000..ad535f4
--- /dev/null
+++ b/wifi/non-updatable/tests/README.md
@@ -0,0 +1,32 @@
+# Wifi Non-Updatable Framework Unit Tests
+This package contains unit tests for the non-updatable part (i.e. outside the Wifi module) of the
+Android Wifi framework APIs based on the
+[Android Testing Support Library](http://developer.android.com/tools/testing-support-library/index.html).
+The test cases are built using the [JUnit](http://junit.org/) and [Mockito](http://mockito.org/)
+libraries.
+
+## Running Tests
+The easiest way to run tests is simply run
+
+```
+atest android.net.wifi
+```
+
+To pick up changes in framework/base, you will need to:
+1. rebuild the framework library 'make -j32'
+2. sync over the updated library to the device 'adb sync'
+3. restart framework on the device 'adb shell stop' then 'adb shell start'
+
+To enable syncing data to the device for first time after clean reflash:
+1. adb disable-verity
+2. adb reboot
+3. adb remount
+
+## Adding Tests
+Tests can be added by adding classes to the src directory. JUnit4 style test cases can
+be written by simply annotating test methods with `org.junit.Test`.
+
+## Debugging Tests
+If you are trying to debug why tests are not doing what you expected, you can add android log
+statements and use logcat to view them. The beginning and end of every tests is automatically logged
+with the tag `TestRunner`.
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java b/wifi/non-updatable/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java
similarity index 100%
rename from wifi/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java b/wifi/non-updatable/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
similarity index 99%
rename from wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
index fdd11a3..c4967eb 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
+++ b/wifi/non-updatable/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package android.net.wifi;
diff --git a/wifi/tests/src/android/net/wifi/nl80211/DeviceWiphyCapabilitiesTest.java b/wifi/non-updatable/tests/src/android/net/wifi/nl80211/DeviceWiphyCapabilitiesTest.java
similarity index 100%
rename from wifi/tests/src/android/net/wifi/nl80211/DeviceWiphyCapabilitiesTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/nl80211/DeviceWiphyCapabilitiesTest.java
diff --git a/wifi/tests/src/android/net/wifi/nl80211/NativeScanResultTest.java b/wifi/non-updatable/tests/src/android/net/wifi/nl80211/NativeScanResultTest.java
similarity index 100%
rename from wifi/tests/src/android/net/wifi/nl80211/NativeScanResultTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/nl80211/NativeScanResultTest.java
diff --git a/wifi/tests/src/android/net/wifi/nl80211/PnoSettingsTest.java b/wifi/non-updatable/tests/src/android/net/wifi/nl80211/PnoSettingsTest.java
similarity index 100%
rename from wifi/tests/src/android/net/wifi/nl80211/PnoSettingsTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/nl80211/PnoSettingsTest.java
diff --git a/wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java b/wifi/non-updatable/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java
similarity index 100%
rename from wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java
diff --git a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java b/wifi/non-updatable/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
similarity index 100%
rename from wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java