Merge "Mirrors calls to suspend LE Audio with those for A2DP"
diff --git a/core/api/current.txt b/core/api/current.txt
index b243374..4c1594b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -18727,8 +18727,8 @@
     method public int bulkTransfer(android.hardware.usb.UsbEndpoint, byte[], int, int, int);
     method public boolean claimInterface(android.hardware.usb.UsbInterface, boolean);
     method public void close();
-    method public int controlTransfer(int, int, int, int, byte[], int, int);
-    method public int controlTransfer(int, int, int, int, byte[], int, int, int);
+    method public int controlTransfer(int, int, int, int, @Nullable byte[], int, int);
+    method public int controlTransfer(int, int, int, int, @Nullable byte[], int, int, int);
     method public int getFileDescriptor();
     method public byte[] getRawDescriptors();
     method public String getSerial();
@@ -41362,6 +41362,8 @@
     field public static final String KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY = "carrier_nr_availabilities_int_array";
     field public static final String KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL = "carrier_provisions_wifi_merged_networks_bool";
     field public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL = "carrier_rcs_provisioning_required_bool";
+    field public static final String KEY_CARRIER_SERVICE_NAME_STRING_ARRAY = "carrier_service_name_array";
+    field public static final String KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY = "carrier_service_number_array";
     field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string";
     field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
     field public static final String KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL = "carrier_supports_opp_data_auto_provisioning_bool";
@@ -42743,9 +42745,12 @@
     method public int getDomain();
     method @Nullable public String getRegisteredPlmn();
     method public int getTransportType();
-    method public boolean isRegistered();
-    method public boolean isRoaming();
-    method public boolean isSearching();
+    method public boolean isNetworkRegistered();
+    method public boolean isNetworkRoaming();
+    method public boolean isNetworkSearching();
+    method @Deprecated public boolean isRegistered();
+    method @Deprecated public boolean isRoaming();
+    method @Deprecated public boolean isSearching();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.NetworkRegistrationInfo> CREATOR;
     field public static final int DOMAIN_CS = 1; // 0x1
@@ -43499,6 +43504,7 @@
     method public int describeContents();
     method public int getNetworkType();
     method public int getOverrideNetworkType();
+    method public boolean isRoaming();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.TelephonyDisplayInfo> CREATOR;
     field public static final int OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO = 2; // 0x2
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 398b596..821944f 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -12828,7 +12828,8 @@
 
   public final class NetworkRegistrationInfo implements android.os.Parcelable {
     method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo();
-    method public int getRegistrationState();
+    method public int getNetworkRegistrationState();
+    method @Deprecated public int getRegistrationState();
     method public int getRejectCause();
     method public int getRoamingType();
     method public boolean isEmergencyEnabled();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5599893..5ba7a4c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -238,6 +238,7 @@
 import java.lang.ref.WeakReference;
 import java.lang.reflect.Method;
 import java.net.InetAddress;
+import java.nio.file.DirectoryStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.StandardCopyOption;
@@ -4221,18 +4222,20 @@
 
     static void handleAttachStartupAgents(String dataDir) {
         try {
-            Path code_cache = ContextImpl.getCodeCacheDirBeforeBind(new File(dataDir)).toPath();
-            if (!Files.exists(code_cache)) {
+            Path codeCache = ContextImpl.getCodeCacheDirBeforeBind(new File(dataDir)).toPath();
+            if (!Files.exists(codeCache)) {
                 return;
             }
-            Path startup_path = code_cache.resolve("startup_agents");
-            if (Files.exists(startup_path)) {
-                for (Path p : Files.newDirectoryStream(startup_path)) {
-                    handleAttachAgent(
-                            p.toAbsolutePath().toString()
-                            + "="
-                            + dataDir,
-                            null);
+            Path startupPath = codeCache.resolve("startup_agents");
+            if (Files.exists(startupPath)) {
+                try (DirectoryStream<Path> startupFiles = Files.newDirectoryStream(startupPath)) {
+                    for (Path p : startupFiles) {
+                        handleAttachAgent(
+                                p.toAbsolutePath().toString()
+                                        + "="
+                                        + dataDir,
+                                null);
+                    }
                 }
             }
         } catch (Exception e) {
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index bccbb38..2a854b2 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -37,8 +37,6 @@
 import android.os.PowerExemptionManager.ReasonCode;
 import android.os.PowerExemptionManager.TempAllowListType;
 
-import com.android.internal.util.Preconditions;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
@@ -61,7 +59,8 @@
     private long mRequireCompatChangeId = CHANGE_INVALID;
     private long mIdForResponseEvent;
     private @DeliveryGroupPolicy int mDeliveryGroupPolicy;
-    private @Nullable String mDeliveryGroupMatchingKey;
+    private @Nullable String mDeliveryGroupMatchingNamespaceFragment;
+    private @Nullable String mDeliveryGroupMatchingKeyFragment;
     private @Nullable BundleMerger mDeliveryGroupExtrasMerger;
     private @Nullable IntentFilter mDeliveryGroupMatchingFilter;
     private @DeferralPolicy int mDeferralPolicy;
@@ -209,7 +208,13 @@
             "android:broadcast.deliveryGroupPolicy";
 
     /**
-     * Corresponds to {@link #setDeliveryGroupMatchingKey(String, String)}.
+     * Corresponds to namespace fragment of {@link #setDeliveryGroupMatchingKey(String, String)}.
+     */
+    private static final String KEY_DELIVERY_GROUP_NAMESPACE =
+            "android:broadcast.deliveryGroupMatchingNamespace";
+
+    /**
+     * Corresponds to key fragment of {@link #setDeliveryGroupMatchingKey(String, String)}.
      */
     private static final String KEY_DELIVERY_GROUP_KEY =
             "android:broadcast.deliveryGroupMatchingKey";
@@ -350,7 +355,8 @@
         mIdForResponseEvent = opts.getLong(KEY_ID_FOR_RESPONSE_EVENT);
         mDeliveryGroupPolicy = opts.getInt(KEY_DELIVERY_GROUP_POLICY,
                 DELIVERY_GROUP_POLICY_ALL);
-        mDeliveryGroupMatchingKey = opts.getString(KEY_DELIVERY_GROUP_KEY);
+        mDeliveryGroupMatchingNamespaceFragment = opts.getString(KEY_DELIVERY_GROUP_NAMESPACE);
+        mDeliveryGroupMatchingKeyFragment = opts.getString(KEY_DELIVERY_GROUP_KEY);
         mDeliveryGroupExtrasMerger = opts.getParcelable(KEY_DELIVERY_GROUP_EXTRAS_MERGER,
                 BundleMerger.class);
         mDeliveryGroupMatchingFilter = opts.getParcelable(KEY_DELIVERY_GROUP_MATCHING_FILTER,
@@ -864,11 +870,8 @@
     @NonNull
     public BroadcastOptions setDeliveryGroupMatchingKey(@NonNull String namespace,
             @NonNull String key) {
-        Preconditions.checkArgument(!namespace.contains(":"),
-                "namespace should not contain ':'");
-        Preconditions.checkArgument(!key.contains(":"),
-                "key should not contain ':'");
-        mDeliveryGroupMatchingKey = namespace + ":" + key;
+        mDeliveryGroupMatchingNamespaceFragment = Objects.requireNonNull(namespace);
+        mDeliveryGroupMatchingKeyFragment = Objects.requireNonNull(key);
         return this;
     }
 
@@ -881,7 +884,38 @@
      */
     @Nullable
     public String getDeliveryGroupMatchingKey() {
-        return mDeliveryGroupMatchingKey;
+        if (mDeliveryGroupMatchingNamespaceFragment == null
+                || mDeliveryGroupMatchingKeyFragment == null) {
+            return null;
+        }
+        return String.join(":", mDeliveryGroupMatchingNamespaceFragment,
+                mDeliveryGroupMatchingKeyFragment);
+    }
+
+    /**
+     * Return the namespace fragment that is used to identify the delivery group that this
+     * broadcast belongs to.
+     *
+     * @return the delivery group namespace fragment that was previously set using
+     *         {@link #setDeliveryGroupMatchingKey(String, String)}.
+     * @hide
+     */
+    @Nullable
+    public String getDeliveryGroupMatchingNamespaceFragment() {
+        return mDeliveryGroupMatchingNamespaceFragment;
+    }
+
+    /**
+     * Return the key fragment that is used to identify the delivery group that this
+     * broadcast belongs to.
+     *
+     * @return the delivery group key fragment that was previously set using
+     *         {@link #setDeliveryGroupMatchingKey(String, String)}.
+     * @hide
+     */
+    @Nullable
+    public String getDeliveryGroupMatchingKeyFragment() {
+        return mDeliveryGroupMatchingKeyFragment;
     }
 
     /**
@@ -889,7 +923,8 @@
      * {@link #setDeliveryGroupMatchingKey(String, String)}.
      */
     public void clearDeliveryGroupMatchingKey() {
-        mDeliveryGroupMatchingKey = null;
+        mDeliveryGroupMatchingNamespaceFragment = null;
+        mDeliveryGroupMatchingKeyFragment = null;
     }
 
     /**
@@ -1101,8 +1136,11 @@
         if (mDeliveryGroupPolicy != DELIVERY_GROUP_POLICY_ALL) {
             b.putInt(KEY_DELIVERY_GROUP_POLICY, mDeliveryGroupPolicy);
         }
-        if (mDeliveryGroupMatchingKey != null) {
-            b.putString(KEY_DELIVERY_GROUP_KEY, mDeliveryGroupMatchingKey);
+        if (mDeliveryGroupMatchingNamespaceFragment != null) {
+            b.putString(KEY_DELIVERY_GROUP_NAMESPACE, mDeliveryGroupMatchingNamespaceFragment);
+        }
+        if (mDeliveryGroupMatchingKeyFragment != null) {
+            b.putString(KEY_DELIVERY_GROUP_KEY, mDeliveryGroupMatchingKeyFragment);
         }
         if (mDeliveryGroupPolicy == DELIVERY_GROUP_POLICY_MERGED) {
             if (mDeliveryGroupExtrasMerger != null) {
diff --git a/core/java/android/hardware/soundtrigger/OWNERS b/core/java/android/hardware/soundtrigger/OWNERS
index 01b2cb9..1e41886 100644
--- a/core/java/android/hardware/soundtrigger/OWNERS
+++ b/core/java/android/hardware/soundtrigger/OWNERS
@@ -1,2 +1 @@
-atneya@google.com
-elaurent@google.com
+include /media/java/android/media/soundtrigger/OWNERS
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index 7c2e518..44144d9 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -238,7 +238,7 @@
      * or negative value for failure
      */
     public int controlTransfer(int requestType, int request, int value,
-            int index, byte[] buffer, int length, int timeout) {
+            int index, @Nullable byte[] buffer, int length, int timeout) {
         return controlTransfer(requestType, request, value, index, buffer, 0, length, timeout);
     }
 
@@ -263,7 +263,7 @@
      * or negative value for failure
      */
     public int controlTransfer(int requestType, int request, int value, int index,
-            byte[] buffer, int offset, int length, int timeout) {
+            @Nullable byte[] buffer, int offset, int length, int timeout) {
         checkBounds(buffer, offset, length);
         return native_control_request(requestType, request, value, index,
                 buffer, offset, length, timeout);
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index a20191c..74775a8 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -769,6 +769,19 @@
         }
     }
 
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("IkeV2VpnProfile [");
+        sb.append(" MaxMtu=" + mMaxMtu);
+        if (mIsBypassable) sb.append(" Bypassable");
+        if (mRequiresInternetValidation) sb.append(" RequiresInternetValidation");
+        if (mIsRestrictedToTestNetworks) sb.append(" RestrictedToTestNetworks");
+        if (mAutomaticNattKeepaliveTimerEnabled) sb.append(" AutomaticNattKeepaliveTimerEnabled");
+        if (mAutomaticIpVersionSelectionEnabled) sb.append(" AutomaticIpVersionSelectionEnabled");
+        sb.append("]");
+        return sb.toString();
+    }
+
     /** A incremental builder for IKEv2 VPN profiles */
     public static final class Builder {
         private int mType = -1;
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
index 38b3174..46cf016 100644
--- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
@@ -354,6 +354,7 @@
     }
 
     /** @hide */
+    @Override
     public Map<Integer, Integer> getCapabilitiesMatchCriteria() {
         return Collections.unmodifiableMap(new HashMap<>(mCapabilitiesMatchCriteria));
     }
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
index 9235d09..edf2c09 100644
--- a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
@@ -29,6 +29,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -307,4 +308,7 @@
     public int getMinExitDownstreamBandwidthKbps() {
         return mMinExitDownstreamBandwidthKbps;
     }
+
+    /** @hide */
+    public abstract Map<Integer, Integer> getCapabilitiesMatchCriteria();
 }
diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
index 2544a6d..2e6b09f 100644
--- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
@@ -15,6 +15,9 @@
  */
 package android.net.vcn;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
 import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;
 import static com.android.server.vcn.util.PersistableBundleUtils.STRING_SERIALIZER;
@@ -23,6 +26,7 @@
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.net.NetworkCapabilities;
+import android.net.vcn.VcnUnderlyingNetworkTemplate.MatchCriteria;
 import android.os.PersistableBundle;
 import android.util.ArraySet;
 
@@ -32,6 +36,7 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
@@ -162,6 +167,12 @@
         return Collections.unmodifiableSet(mSsids);
     }
 
+    /** @hide */
+    @Override
+    public Map<Integer, Integer> getCapabilitiesMatchCriteria() {
+        return Collections.singletonMap(NET_CAPABILITY_INTERNET, MATCH_REQUIRED);
+    }
+
     /** This class is used to incrementally build VcnWifiUnderlyingNetworkTemplate objects. */
     public static final class Builder {
         private int mMeteredMatchCriteria = MATCH_ANY;
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 3c1b4ba..0012572 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -8410,7 +8410,7 @@
             extras.putString(KEY_ACCOUNT_NAME, accountName);
             extras.putString(KEY_ACCOUNT_TYPE, accountType);
 
-            contentResolver.call(ContactsContract.AUTHORITY_URI,
+            nullSafeCall(contentResolver, ContactsContract.AUTHORITY_URI,
                     ContactsContract.SimContacts.ADD_SIM_ACCOUNT_METHOD,
                     null, extras);
         }
@@ -8433,7 +8433,7 @@
             Bundle extras = new Bundle();
             extras.putInt(KEY_SIM_SLOT_INDEX, simSlotIndex);
 
-            contentResolver.call(ContactsContract.AUTHORITY_URI,
+            nullSafeCall(contentResolver, ContactsContract.AUTHORITY_URI,
                     ContactsContract.SimContacts.REMOVE_SIM_ACCOUNT_METHOD,
                     null, extras);
         }
@@ -8445,7 +8445,7 @@
          */
         public static @NonNull List<SimAccount> getSimAccounts(
                 @NonNull ContentResolver contentResolver) {
-            Bundle response = contentResolver.call(ContactsContract.AUTHORITY_URI,
+            Bundle response = nullSafeCall(contentResolver, ContactsContract.AUTHORITY_URI,
                     ContactsContract.SimContacts.QUERY_SIM_ACCOUNTS_METHOD,
                     null, null);
             List<SimAccount> result = response.getParcelableArrayList(KEY_SIM_ACCOUNTS);
@@ -9064,7 +9064,8 @@
          * @param contactId the id of the contact to undemote.
          */
         public static void undemote(ContentResolver contentResolver, long contactId) {
-            contentResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD,
+            nullSafeCall(contentResolver, ContactsContract.AUTHORITY_URI,
+                    PinnedPositions.UNDEMOTE_METHOD,
                     String.valueOf(contactId), null);
         }
 
@@ -10276,4 +10277,13 @@
         public static final String CONTENT_ITEM_TYPE =
                 "vnd.android.cursor.item/contact_metadata_sync_state";
     }
+
+    private static Bundle nullSafeCall(@NonNull ContentResolver resolver, @NonNull Uri uri,
+            @NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
+        try (ContentProviderClient client = resolver.acquireContentProviderClient(uri)) {
+            return client.call(method, arg, extras);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
 }
diff --git a/core/java/com/android/internal/expresslog/Histogram.java b/core/java/com/android/internal/expresslog/Histogram.java
index 65fbb03..2fe784a 100644
--- a/core/java/com/android/internal/expresslog/Histogram.java
+++ b/core/java/com/android/internal/expresslog/Histogram.java
@@ -54,6 +54,19 @@
                 /*count*/ 1, binIndex);
     }
 
+    /**
+     * Logs increment sample count for automatically calculated bin
+     *
+     * @param uid used as a dimension for the count metric
+     * @param sample value
+     * @hide
+     */
+    public void logSampleWithUid(int uid, float sample) {
+        final int binIndex = mBinOptions.getBinForSample(sample);
+        FrameworkStatsLog.write(FrameworkStatsLog.EXPRESS_UID_HISTOGRAM_SAMPLE_REPORTED,
+                mMetricIdHash, /*count*/ 1, binIndex, uid);
+    }
+
     /** Used by Histogram to map data sample to corresponding bin */
     public interface BinOptions {
         /**
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index a7c7d0b..b24dc8a 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -413,9 +413,13 @@
 
         if (env->ExceptionCheck()) {
             ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred());
-            binder_report_exception(env, excep.get(),
-                                    "*** Uncaught remote exception!  "
-                                    "(Exceptions are not yet supported across processes.)");
+
+            auto state = IPCThreadState::self();
+            String8 msg;
+            msg.appendFormat("*** Uncaught remote exception! Exceptions are not yet supported "
+                             "across processes. Client PID %d UID %d.",
+                             state->getCallingPid(), state->getCallingUid());
+            binder_report_exception(env, excep.get(), msg.c_str());
             res = JNI_FALSE;
         }
 
@@ -431,6 +435,7 @@
             ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred());
             binder_report_exception(env, excep.get(),
                                     "*** Uncaught exception in onBinderStrictModePolicyChange");
+            // TODO: should turn this to fatal?
         }
 
         // Need to always call through the native implementation of
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a5e9617..277bdf3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1334,8 +1334,7 @@
         android:permissionFlags="hardRestricted"
         android:protectionLevel="dangerous" />
 
-    <!-- Allows an application to write (but not read) the user's
-         call log data.
+    <!-- Allows an application to write and read the user's call log data.
          <p class="note"><strong>Note:</strong> If your app uses the
          {@link #WRITE_CONTACTS} permission and <em>both</em> your <a
          href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index c2cd6ff..74597c5 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -161,6 +161,15 @@
     }
 
     /**
+     * List all entries in the keystore for in the given namespace.
+     */
+    public KeyDescriptor[] listBatch(int domain, long namespace, String startPastAlias)
+            throws KeyStoreException {
+        return handleRemoteExceptionWithRetry(
+                (service) -> service.listEntriesBatched(domain, namespace, startPastAlias));
+    }
+
+    /**
      * Grant string prefix as used by the keystore boringssl engine. Must be kept in sync
      * with system/security/keystore-engine. Note: The prefix here includes the 0x which
      * std::stringstream used in keystore-engine needs to identify the number as hex represented.
@@ -301,6 +310,13 @@
         });
     }
 
+    /**
+     * Returns the number of Keystore entries for a given domain and namespace.
+     */
+    public int getNumberOfEntries(int domain, long namespace) throws KeyStoreException {
+        return handleRemoteExceptionWithRetry((service)
+                -> service.getNumberOfEntries(domain, namespace));
+    }
     protected static void interruptedPreservingSleep(long millis) {
         boolean wasInterrupted = false;
         Calendar calendar = Calendar.getInstance();
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 91f216f..045e318 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -79,13 +79,11 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Date;
 import java.util.Enumeration;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Set;
+import java.util.NoSuchElementException;
 
 import javax.crypto.SecretKey;
 
@@ -1043,26 +1041,22 @@
         }
     }
 
-    private Set<String> getUniqueAliases() {
+    private KeyDescriptor[] getAliasesBatch(String startPastAlias) {
         try {
-            final KeyDescriptor[] keys = mKeyStore.list(
+            return mKeyStore.listBatch(
                     getTargetDomain(),
-                    mNamespace
+                    mNamespace,
+                    startPastAlias
             );
-            final Set<String> aliases = new HashSet<>(keys.length);
-            for (KeyDescriptor d : keys) {
-                aliases.add(d.alias);
-            }
-            return aliases;
         } catch (android.security.KeyStoreException e) {
             Log.e(TAG, "Failed to list keystore entries.", e);
-            return new HashSet<>();
+            return new KeyDescriptor[0];
         }
     }
 
     @Override
     public Enumeration<String> engineAliases() {
-        return Collections.enumeration(getUniqueAliases());
+        return new KeyEntriesEnumerator();
     }
 
     @Override
@@ -1073,12 +1067,18 @@
 
         return getKeyMetadata(alias) != null;
     }
-
     @Override
     public int engineSize() {
-        return getUniqueAliases().size();
+        try {
+            return mKeyStore.getNumberOfEntries(
+                    getTargetDomain(),
+                    mNamespace
+            );
+        } catch (android.security.KeyStoreException e) {
+            Log.e(TAG, "Failed to get the number of keystore entries.", e);
+            return 0;
+        }
     }
-
     @Override
     public boolean engineIsKeyEntry(String alias) {
         return isKeyEntry(alias);
@@ -1251,4 +1251,38 @@
                             + "or TrustedCertificateEntry; was " + entry);
         }
     }
+
+    private class KeyEntriesEnumerator implements Enumeration<String> {
+        private KeyDescriptor[] mCurrentBatch;
+        private int mCurrentEntry = 0;
+        private String mLastAlias = null;
+        private KeyEntriesEnumerator() {
+            getAndValidateNextBatch();
+        }
+
+        private void getAndValidateNextBatch() {
+            mCurrentBatch = getAliasesBatch(mLastAlias);
+            mCurrentEntry = 0;
+        }
+
+        public boolean hasMoreElements() {
+            return (mCurrentBatch != null) && (mCurrentBatch.length > 0);
+        }
+
+        public String nextElement() {
+            if ((mCurrentBatch == null) || (mCurrentBatch.length == 0)) {
+                throw new NoSuchElementException("Error while fetching entries.");
+            }
+            final KeyDescriptor currentEntry = mCurrentBatch[mCurrentEntry];
+            mLastAlias = currentEntry.alias;
+
+            mCurrentEntry++;
+            // This was the last entry in the batch.
+            if (mCurrentEntry >= mCurrentBatch.length) {
+                getAndValidateNextBatch();
+            }
+
+            return mLastAlias;
+        }
+    }
 }
diff --git a/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java
index f96c39c8..1e1f68a 100644
--- a/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java
+++ b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java
@@ -17,9 +17,14 @@
 package android.security.keystore2;
 
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertThrows;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.isNull;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.security.KeyStore2;
@@ -36,6 +41,12 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.NoSuchElementException;
+
 public class AndroidKeyStoreSpiTest {
 
     @Mock
@@ -48,14 +59,176 @@
 
     @Test
     public void testEngineAliasesReturnsEmptySetOnKeyStoreError() throws Exception {
-        when(mKeystore2.list(anyInt(), anyLong()))
+        when(mKeystore2.listBatch(anyInt(), anyLong(), isNull()))
                 .thenThrow(new KeyStoreException(6, "Some Error"));
         AndroidKeyStoreSpi spi = new AndroidKeyStoreSpi();
         spi.initForTesting(mKeystore2);
 
         assertThat("Empty collection expected", !spi.engineAliases().hasMoreElements());
 
-        verify(mKeystore2).list(anyInt(), anyLong());
+        verify(mKeystore2).listBatch(anyInt(), anyLong(), isNull());
+    }
+
+    @Test
+    public void testEngineAliasesCorrectlyListsZeroEntriesEmptyArray() throws Exception {
+        when(mKeystore2.listBatch(anyInt(), anyLong(), anyString()))
+                .thenReturn(new KeyDescriptor[0]);
+        AndroidKeyStoreSpi spi = new AndroidKeyStoreSpi();
+        spi.initForTesting(mKeystore2);
+
+        Enumeration<String> aliases = spi.engineAliases();
+        assertThat("Should not have any elements", !aliases.hasMoreElements());
+        assertThrows("Should have no elements to return", NoSuchElementException.class,
+                () -> aliases.nextElement());
+        verify(mKeystore2).listBatch(anyInt(), anyLong(), isNull());
+    }
+
+    @Test
+    public void testEngineAliasesCorrectlyListsZeroEntriesNullArray() throws Exception {
+        when(mKeystore2.listBatch(anyInt(), anyLong(), anyString()))
+                .thenReturn(null);
+        AndroidKeyStoreSpi spi = new AndroidKeyStoreSpi();
+        spi.initForTesting(mKeystore2);
+
+        Enumeration<String> aliases = spi.engineAliases();
+        assertThat("Should not have any elements", !aliases.hasMoreElements());
+        assertThrows("Should have no elements to return", NoSuchElementException.class,
+                () -> aliases.nextElement());
+        verify(mKeystore2).listBatch(anyInt(), anyLong(), isNull());
+    }
+
+    private static KeyDescriptor newKeyDescriptor(String alias) {
+        KeyDescriptor result = new KeyDescriptor();
+        result.alias = alias;
+        return result;
+    }
+
+    private static KeyDescriptor[] createKeyDescriptorsArray(int numEntries) {
+        KeyDescriptor[] kds = new KeyDescriptor[numEntries];
+        for (int i = 0; i < kds.length; i++) {
+            kds[i] = newKeyDescriptor(String.format("alias-%d", i));
+        }
+
+        return kds;
+    }
+
+    private static void assertAliasListsEqual(
+            KeyDescriptor[] keyDescriptors, Enumeration<String> aliasesEnumerator) {
+        List<String> aliases = Collections.list(aliasesEnumerator);
+        Assert.assertArrayEquals(Arrays.stream(keyDescriptors).map(kd -> kd.alias).toArray(),
+                aliases.toArray());
+    }
+
+    @Test
+    public void testEngineAliasesCorrectlyListsEntriesInASingleBatch() throws Exception {
+        final String alias1 = "testAlias1";
+        final String alias2 = "testAlias2";
+        final String alias3 = "testAlias3";
+        KeyDescriptor[] kds = {newKeyDescriptor(alias1),
+                newKeyDescriptor(alias2), newKeyDescriptor(alias3)};
+        when(mKeystore2.listBatch(anyInt(), anyLong(), eq(null)))
+                .thenReturn(kds);
+        when(mKeystore2.listBatch(anyInt(), anyLong(), eq("testAlias3")))
+                .thenReturn(null);
+
+        AndroidKeyStoreSpi spi = new AndroidKeyStoreSpi();
+        spi.initForTesting(mKeystore2);
+
+        Enumeration<String> aliases = spi.engineAliases();
+        assertThat("Should have more elements before first.", aliases.hasMoreElements());
+        Assert.assertEquals(aliases.nextElement(), alias1);
+        assertThat("Should have more elements before second.", aliases.hasMoreElements());
+        Assert.assertEquals(aliases.nextElement(), alias2);
+        assertThat("Should have more elements before third.", aliases.hasMoreElements());
+        Assert.assertEquals(aliases.nextElement(), alias3);
+        assertThat("Should have no more elements after third.", !aliases.hasMoreElements());
+        verify(mKeystore2).listBatch(anyInt(), anyLong(), eq(null));
+        verify(mKeystore2).listBatch(anyInt(), anyLong(), eq("testAlias3"));
+        verifyNoMoreInteractions(mKeystore2);
+    }
+
+    @Test
+    public void testEngineAliasesCorrectlyListsEntriesInMultipleBatches() throws Exception {
+        final int numEntries = 2500;
+        KeyDescriptor[] kds = createKeyDescriptorsArray(numEntries);
+        when(mKeystore2.listBatch(anyInt(), anyLong(), eq(null)))
+                .thenReturn(Arrays.copyOfRange(kds, 0, 1000));
+        when(mKeystore2.listBatch(anyInt(), anyLong(), eq("alias-999")))
+                .thenReturn(Arrays.copyOfRange(kds, 1000, 2000));
+        when(mKeystore2.listBatch(anyInt(), anyLong(), eq("alias-1999")))
+                .thenReturn(Arrays.copyOfRange(kds, 2000, 2500));
+        when(mKeystore2.listBatch(anyInt(), anyLong(), eq("alias-2499")))
+                .thenReturn(null);
+
+        AndroidKeyStoreSpi spi = new AndroidKeyStoreSpi();
+        spi.initForTesting(mKeystore2);
+
+        assertAliasListsEqual(kds, spi.engineAliases());
+        verify(mKeystore2).listBatch(anyInt(), anyLong(), eq(null));
+        verify(mKeystore2).listBatch(anyInt(), anyLong(), eq("alias-999"));
+        verify(mKeystore2).listBatch(anyInt(), anyLong(), eq("alias-1999"));
+        verify(mKeystore2).listBatch(anyInt(), anyLong(), eq("alias-2499"));
+        verifyNoMoreInteractions(mKeystore2);
+    }
+
+    @Test
+    public void testEngineAliasesCorrectlyListsEntriesWhenNumEntriesIsExactlyOneBatchSize()
+            throws Exception {
+        final int numEntries = 1000;
+        KeyDescriptor[] kds = createKeyDescriptorsArray(numEntries);
+        when(mKeystore2.listBatch(anyInt(), anyLong(), eq(null)))
+                .thenReturn(kds);
+        when(mKeystore2.listBatch(anyInt(), anyLong(), eq("alias-999")))
+                .thenReturn(null);
+
+        AndroidKeyStoreSpi spi = new AndroidKeyStoreSpi();
+        spi.initForTesting(mKeystore2);
+
+        assertAliasListsEqual(kds, spi.engineAliases());
+        verify(mKeystore2).listBatch(anyInt(), anyLong(), eq(null));
+        verify(mKeystore2).listBatch(anyInt(), anyLong(), eq("alias-999"));
+        verifyNoMoreInteractions(mKeystore2);
+    }
+
+    @Test
+    public void testEngineAliasesCorrectlyListsEntriesWhenNumEntriesIsAMultiplyOfBatchSize()
+            throws Exception {
+        final int numEntries = 2000;
+        KeyDescriptor[] kds = createKeyDescriptorsArray(numEntries);
+        when(mKeystore2.listBatch(anyInt(), anyLong(), eq(null)))
+                .thenReturn(Arrays.copyOfRange(kds, 0, 1000));
+        when(mKeystore2.listBatch(anyInt(), anyLong(), eq("alias-999")))
+                .thenReturn(Arrays.copyOfRange(kds, 1000, 2000));
+        when(mKeystore2.listBatch(anyInt(), anyLong(), eq("alias-1999")))
+                .thenReturn(null);
+
+        AndroidKeyStoreSpi spi = new AndroidKeyStoreSpi();
+        spi.initForTesting(mKeystore2);
+
+        assertAliasListsEqual(kds, spi.engineAliases());
+        verify(mKeystore2).listBatch(anyInt(), anyLong(), eq(null));
+        verify(mKeystore2).listBatch(anyInt(), anyLong(), eq("alias-999"));
+        verify(mKeystore2).listBatch(anyInt(), anyLong(), eq("alias-1999"));
+        verifyNoMoreInteractions(mKeystore2);
+    }
+
+    @Test
+    public void testEngineAliasesCorrectlyListsEntriesWhenReturningLessThanBatchSize()
+            throws Exception {
+        final int numEntries = 500;
+        KeyDescriptor[] kds = createKeyDescriptorsArray(numEntries);
+        when(mKeystore2.listBatch(anyInt(), anyLong(), eq(null)))
+                .thenReturn(kds);
+        when(mKeystore2.listBatch(anyInt(), anyLong(), eq("alias-499")))
+                .thenReturn(new KeyDescriptor[0]);
+
+        AndroidKeyStoreSpi spi = new AndroidKeyStoreSpi();
+        spi.initForTesting(mKeystore2);
+
+        assertAliasListsEqual(kds, spi.engineAliases());
+        verify(mKeystore2).listBatch(anyInt(), anyLong(), eq(null));
+        verify(mKeystore2).listBatch(anyInt(), anyLong(), eq("alias-499"));
+        verifyNoMoreInteractions(mKeystore2);
     }
 
     @Mock
diff --git a/media/OWNERS b/media/OWNERS
index 5f50137..4a6648e 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -1,4 +1,5 @@
 # Bug component: 1344
+atneya@google.com
 elaurent@google.com
 essick@google.com
 etalvala@google.com
diff --git a/media/aidl/android/media/soundtrigger_middleware/OWNERS b/media/aidl/android/media/soundtrigger_middleware/OWNERS
index 01b2cb9..1e41886 100644
--- a/media/aidl/android/media/soundtrigger_middleware/OWNERS
+++ b/media/aidl/android/media/soundtrigger_middleware/OWNERS
@@ -1,2 +1 @@
-atneya@google.com
-elaurent@google.com
+include /media/java/android/media/soundtrigger/OWNERS
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 22033c6..6b29fc3 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -72,7 +72,7 @@
         throw new UnsupportedOperationException("Trying to instantiate AudioSystem");
     }
 
-    /* These values must be kept in sync with system/audio.h */
+    /* These values must be kept in sync with system/media/audio/include/system/audio-hal-enums.h */
     /*
      * If these are modified, please also update Settings.System.VOLUME_SETTINGS
      * and attrs.xml and AudioManager.java.
@@ -963,7 +963,8 @@
      */
 
     //
-    // audio device definitions: must be kept in sync with values in system/core/audio.h
+    // audio device definitions: must be kept in sync with values
+    // in system/media/audio/include/system/audio-hal-enums.h
     //
     /** @hide */
     public static final int DEVICE_NONE = 0x0;
diff --git a/media/java/android/media/soundtrigger/OWNERS b/media/java/android/media/soundtrigger/OWNERS
index 01b2cb9..85f7a4d 100644
--- a/media/java/android/media/soundtrigger/OWNERS
+++ b/media/java/android/media/soundtrigger/OWNERS
@@ -1,2 +1,3 @@
+# Bug component: 48436
 atneya@google.com
 elaurent@google.com
diff --git a/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java b/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java
index f305fd3..e92157e 100644
--- a/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java
+++ b/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java
@@ -47,7 +47,7 @@
  * Annotation processor for {@link SearchIndexable} that generates {@link SearchIndexableResources}
  * subclasses.
  */
-@SupportedSourceVersion(SourceVersion.RELEASE_11)
+@SupportedSourceVersion(SourceVersion.RELEASE_17)
 @SupportedAnnotationTypes({"com.android.settingslib.search.SearchIndexable"})
 public class IndexableProcessor extends AbstractProcessor {
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
index 39b4b8e..35e3dd3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
@@ -231,7 +231,7 @@
         public SignalStrength signalStrength;
         public TelephonyDisplayInfo telephonyDisplayInfo =
                 new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
+                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false);
 
         /**
          * Empty constructor
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 2e6ea0e..54946ee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -144,7 +144,7 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private static final TelephonyDisplayInfo DEFAULT_TELEPHONY_DISPLAY_INFO =
             new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                    TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
+                    TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false);
 
     static final int MAX_WIFI_ENTRY_COUNT = 3;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt
index 1fb6a98..c37b01f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt
@@ -49,7 +49,7 @@
 ) : ConnectivityState() {
 
     @JvmField var telephonyDisplayInfo = TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
-            TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE)
+            TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false)
     @JvmField var serviceState: ServiceState? = null
     @JvmField var signalStrength: SignalStrength? = null
 
@@ -131,7 +131,7 @@
     }
 
     fun isRoaming(): Boolean {
-        return serviceState != null && serviceState!!.roaming
+        return telephonyDisplayInfo != null && telephonyDisplayInfo.isRoaming
     }
 
     /**
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index ce9c067..2daf04d 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -75,6 +75,9 @@
 import android.content.pm.UserInfo;
 import android.content.res.ObbInfo;
 import android.database.ContentObserver;
+import android.media.MediaCodecList;
+import android.media.MediaCodecInfo;
+import android.media.MediaFormat;
 import android.net.Uri;
 import android.os.BatteryManager;
 import android.os.Binder;
@@ -1006,10 +1009,27 @@
         }
     }
 
+    private boolean isHevcDecoderSupported() {
+        MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+        MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
+        for (MediaCodecInfo codecInfo : codecInfos) {
+            if (codecInfo.isEncoder()) {
+                continue;
+            }
+            String[] supportedTypes = codecInfo.getSupportedTypes();
+            for (String type : supportedTypes) {
+                if (type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     private void configureTranscoding() {
         // See MediaProvider TranscodeHelper#getBooleanProperty for more information
         boolean transcodeEnabled = false;
-        boolean defaultValue = true;
+        boolean defaultValue = isHevcDecoderSupported() ? true : false;
 
         if (SystemProperties.getBoolean("persist.sys.fuse.transcode_user_control", false)) {
             transcodeEnabled = SystemProperties.getBoolean("persist.sys.fuse.transcode_enabled",
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index a69d3f0..77a54a5 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1957,7 +1957,8 @@
                 && overrideNetworkType == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED) {
             overrideNetworkType = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE;
         }
-        return new TelephonyDisplayInfo(networkType, overrideNetworkType);
+        boolean isRoaming = telephonyDisplayInfo.isRoaming();
+        return new TelephonyDisplayInfo(networkType, overrideNetworkType, isRoaming);
     }
 
     public void notifyCallForwardingChanged(boolean cfi) {
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index f652cb0..78d4708 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -1104,7 +1104,7 @@
             final NetworkCapabilities result = ncBuilder.build();
             final VcnUnderlyingNetworkPolicy policy = new VcnUnderlyingNetworkPolicy(
                     mTrackingNetworkCallback
-                            .requiresRestartForImmutableCapabilityChanges(result),
+                            .requiresRestartForImmutableCapabilityChanges(result, linkProperties),
                     result);
 
             logVdbg("getUnderlyingNetworkPolicy() called for caps: " + networkCapabilities
@@ -1354,19 +1354,29 @@
      * without requiring a Network restart.
      */
     private class TrackingNetworkCallback extends ConnectivityManager.NetworkCallback {
+        private final Object mLockObject = new Object();
         private final Map<Network, NetworkCapabilities> mCaps = new ArrayMap<>();
+        private final Map<Network, LinkProperties> mLinkProperties = new ArrayMap<>();
 
         @Override
         public void onCapabilitiesChanged(Network network, NetworkCapabilities caps) {
-            synchronized (mCaps) {
+            synchronized (mLockObject) {
                 mCaps.put(network, caps);
             }
         }
 
         @Override
+        public void onLinkPropertiesChanged(Network network, LinkProperties lp) {
+            synchronized (mLockObject) {
+                mLinkProperties.put(network, lp);
+            }
+        }
+
+        @Override
         public void onLost(Network network) {
-            synchronized (mCaps) {
+            synchronized (mLockObject) {
                 mCaps.remove(network);
+                mLinkProperties.remove(network);
             }
         }
 
@@ -1393,22 +1403,28 @@
             return true;
         }
 
-        private boolean requiresRestartForImmutableCapabilityChanges(NetworkCapabilities caps) {
+        private boolean requiresRestartForImmutableCapabilityChanges(
+                NetworkCapabilities caps, LinkProperties lp) {
             if (caps.getSubscriptionIds() == null) {
                 return false;
             }
 
-            synchronized (mCaps) {
-                for (NetworkCapabilities existing : mCaps.values()) {
-                    if (caps.getSubscriptionIds().equals(existing.getSubscriptionIds())
-                            && hasSameTransportsAndCapabilities(caps, existing)) {
-                        // Restart if any immutable capabilities have changed
-                        return existing.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)
+            synchronized (mLockObject) {
+                // Search for an existing network (using interfce names)
+                // TODO: Get network from NetworkFactory (if exists) for this match.
+                for (Entry<Network, LinkProperties> lpEntry : mLinkProperties.entrySet()) {
+                    if (lp.getInterfaceName() != null
+                            && !lp.getInterfaceName().isEmpty()
+                            && Objects.equals(
+                                    lp.getInterfaceName(), lpEntry.getValue().getInterfaceName())) {
+                        return mCaps.get(lpEntry.getKey())
+                                        .hasCapability(NET_CAPABILITY_NOT_RESTRICTED)
                                 != caps.hasCapability(NET_CAPABILITY_NOT_RESTRICTED);
                     }
                 }
             }
 
+            // If no network found, by definition does not need restart.
             return false;
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d48723a..99f1863 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8415,13 +8415,16 @@
             }
         }
 
+        boolean recoverable = eventType.equals("native_recoverable_crash");
+
         EventLogTags.writeAmCrash(Binder.getCallingPid(),
                 UserHandle.getUserId(Binder.getCallingUid()), processName,
                 r == null ? -1 : r.info.flags,
                 crashInfo.exceptionClassName,
                 crashInfo.exceptionMessage,
                 crashInfo.throwFileName,
-                crashInfo.throwLineNumber);
+                crashInfo.throwLineNumber,
+                recoverable ? 1 : 0);
 
         int processClassEnum = processName.equals("system_server") ? ServerProtoEnums.SYSTEM_SERVER
                 : (r != null) ? r.getProcessClassEnum()
@@ -8489,7 +8492,13 @@
                 eventType, r, processName, null, null, null, null, null, null, crashInfo,
                 new Float(loadingProgress), incrementalMetrics, null);
 
-        mAppErrors.crashApplication(r, crashInfo);
+        // For GWP-ASan recoverable crashes, don't make the app crash (the whole point of
+        // 'recoverable' is that the app doesn't crash). Normally, for nonrecoreable native crashes,
+        // debuggerd will terminate the process, but there's a backup where ActivityManager will
+        // also kill it. Avoid that.
+        if (!recoverable) {
+            mAppErrors.crashApplication(r, crashInfo);
+        }
     }
 
     public void handleApplicationStrictModeViolation(
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index d080036..6ce70a1 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -53,7 +53,7 @@
 30037 am_process_start_timeout (User|1|5),(PID|1|5),(UID|1|5),(Process Name|3)
 
 # Unhandled exception
-30039 am_crash (User|1|5),(PID|1|5),(Process Name|3),(Flags|1|5),(Exception|3),(Message|3),(File|3),(Line|1|5)
+30039 am_crash (User|1|5),(PID|1|5),(Process Name|3),(Flags|1|5),(Exception|3),(Message|3),(File|3),(Line|1|5),(Recoverable|1|5)
 # Log.wtf() called
 30040 am_wtf (User|1|5),(PID|1|5),(Process Name|3),(Flags|1|5),(Tag|3),(Message|3)
 
diff --git a/services/core/java/com/android/server/am/NativeCrashListener.java b/services/core/java/com/android/server/am/NativeCrashListener.java
index 94eb076..cd119e7 100644
--- a/services/core/java/com/android/server/am/NativeCrashListener.java
+++ b/services/core/java/com/android/server/am/NativeCrashListener.java
@@ -64,12 +64,15 @@
     class NativeCrashReporter extends Thread {
         ProcessRecord mApp;
         int mSignal;
+        boolean mGwpAsanRecoverableCrash;
         String mCrashReport;
 
-        NativeCrashReporter(ProcessRecord app, int signal, String report) {
+        NativeCrashReporter(ProcessRecord app, int signal, boolean gwpAsanRecoverableCrash,
+                            String report) {
             super("NativeCrashReport");
             mApp = app;
             mSignal = signal;
+            mGwpAsanRecoverableCrash = gwpAsanRecoverableCrash;
             mCrashReport = report;
         }
 
@@ -85,7 +88,9 @@
                 ci.stackTrace = mCrashReport;
 
                 if (DEBUG) Slog.v(TAG, "Calling handleApplicationCrash()");
-                mAm.handleApplicationCrashInner("native_crash", mApp, mApp.processName, ci);
+                mAm.handleApplicationCrashInner(
+                        mGwpAsanRecoverableCrash ? "native_recoverable_crash" : "native_crash",
+                        mApp, mApp.processName, ci);
                 if (DEBUG) Slog.v(TAG, "<-- handleApplicationCrash() returned");
             } catch (Exception e) {
                 Slog.e(TAG, "Unable to report native crash", e);
@@ -207,9 +212,14 @@
             // permits crash_dump to connect to it. This allows us to trust the
             // received values.
 
-            // first, the pid and signal number
-            int headerBytes = readExactly(fd, buf, 0, 8);
-            if (headerBytes != 8) {
+            // Activity Manager protocol:
+            //  - 32-bit network-byte-order: pid
+            //  - 32-bit network-byte-order: signal number
+            //  - byte: gwpAsanRecoverableCrash
+            //  - bytes: raw text of the dump
+            //  - null terminator
+            int headerBytes = readExactly(fd, buf, 0, 9);
+            if (headerBytes != 9) {
                 // protocol failure; give up
                 Slog.e(TAG, "Unable to read from debuggerd");
                 return;
@@ -217,69 +227,76 @@
 
             int pid = unpackInt(buf, 0);
             int signal = unpackInt(buf, 4);
+            boolean gwpAsanRecoverableCrash = buf[8] != 0;
             if (DEBUG) {
-                Slog.v(TAG, "Read pid=" + pid + " signal=" + signal);
+                Slog.v(TAG, "Read pid=" + pid + " signal=" + signal
+                        + " recoverable=" + gwpAsanRecoverableCrash);
+            }
+            if (pid < 0) {
+                Slog.e(TAG, "Bogus pid!");
+                return;
             }
 
             // now the text of the dump
-            if (pid > 0) {
-                final ProcessRecord pr;
-                synchronized (mAm.mPidsSelfLocked) {
-                    pr = mAm.mPidsSelfLocked.get(pid);
-                }
-                if (pr != null) {
-                    // Don't attempt crash reporting for persistent apps
-                    if (pr.isPersistent()) {
-                        if (DEBUG) {
-                            Slog.v(TAG, "Skipping report for persistent app " + pr);
-                        }
-                        return;
-                    }
-
-                    int bytes;
-                    do {
-                        // get some data
-                        bytes = Os.read(fd, buf, 0, buf.length);
-                        if (bytes > 0) {
-                            if (MORE_DEBUG) {
-                                String s = new String(buf, 0, bytes, "UTF-8");
-                                Slog.v(TAG, "READ=" + bytes + "> " + s);
-                            }
-                            // did we just get the EOD null byte?
-                            if (buf[bytes-1] == 0) {
-                                os.write(buf, 0, bytes-1);  // exclude the EOD token
-                                break;
-                            }
-                            // no EOD, so collect it and read more
-                            os.write(buf, 0, bytes);
-                        }
-                    } while (bytes > 0);
-
-                    // Okay, we've got the report.
-                    if (DEBUG) Slog.v(TAG, "processing");
-
-                    // Mark the process record as being a native crash so that the
-                    // cleanup mechanism knows we're still submitting the report
-                    // even though the process will vanish as soon as we let
-                    // debuggerd proceed.
-                    synchronized (mAm) {
-                        synchronized (mAm.mProcLock) {
-                            pr.mErrorState.setCrashing(true);
-                            pr.mErrorState.setForceCrashReport(true);
-                        }
-                    }
-
-                    // Crash reporting is synchronous but we want to let debuggerd
-                    // go about it business right away, so we spin off the actual
-                    // reporting logic on a thread and let it take it's time.
-                    final String reportString = new String(os.toByteArray(), "UTF-8");
-                    (new NativeCrashReporter(pr, signal, reportString)).start();
-                } else {
-                    Slog.w(TAG, "Couldn't find ProcessRecord for pid " + pid);
-                }
-            } else {
-                Slog.e(TAG, "Bogus pid!");
+            final ProcessRecord pr;
+            synchronized (mAm.mPidsSelfLocked) {
+                pr = mAm.mPidsSelfLocked.get(pid);
             }
+            if (pr == null) {
+                Slog.w(TAG, "Couldn't find ProcessRecord for pid " + pid);
+                return;
+            }
+
+            // Don't attempt crash reporting for persistent apps
+            if (pr.isPersistent()) {
+                if (DEBUG) {
+                    Slog.v(TAG, "Skipping report for persistent app " + pr);
+                }
+                return;
+            }
+
+            int bytes;
+            do {
+                // get some data
+                bytes = Os.read(fd, buf, 0, buf.length);
+                if (bytes > 0) {
+                    if (MORE_DEBUG) {
+                        String s = new String(buf, 0, bytes, "UTF-8");
+                        Slog.v(TAG, "READ=" + bytes + "> " + s);
+                    }
+                    // did we just get the EOD null byte?
+                    if (buf[bytes - 1] == 0) {
+                        os.write(buf, 0, bytes - 1); // exclude the EOD token
+                        break;
+                    }
+                    // no EOD, so collect it and read more
+                    os.write(buf, 0, bytes);
+                }
+            } while (bytes > 0);
+
+            // Okay, we've got the report.
+            if (DEBUG) Slog.v(TAG, "processing");
+
+            // Mark the process record as being a native crash so that the
+            // cleanup mechanism knows we're still submitting the report even
+            // though the process will vanish as soon as we let debuggerd
+            // proceed. This isn't relevant for recoverable crashes, as we don't
+            // show the user an "app crashed" dialogue because the app (by
+            // design) didn't crash.
+            if (!gwpAsanRecoverableCrash) {
+                synchronized (mAm) {
+                    synchronized (mAm.mProcLock) {
+                        pr.mErrorState.setCrashing(true);
+                        pr.mErrorState.setForceCrashReport(true);
+                    }
+                }
+            }
+
+            // Crash reporting is synchronous but we want to let debuggerd
+            // go about it business right away, so we spin off the actual
+            // reporting logic on a thread and let it take it's time.
+            final String reportString = new String(os.toByteArray(), "UTF-8");
+            (new NativeCrashReporter(pr, signal, gwpAsanRecoverableCrash, reportString)).start();
         } catch (Exception e) {
             Slog.e(TAG, "Exception dealing with report", e);
             // ugh, fail.
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index abddc43..256df98 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2573,7 +2573,10 @@
                     + ", " + reason);
             app.setPendingStart(false);
             killProcessQuiet(pid);
-            Process.killProcessGroup(app.uid, app.getPid());
+            final int appPid = app.getPid();
+            if (appPid != 0) {
+                Process.killProcessGroup(app.uid, appPid);
+            }
             noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
                     ApplicationExitInfo.SUBREASON_INVALID_START, reason);
             return false;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 46ab330..a7c2ddf 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -896,7 +896,7 @@
 
     // Defines the format for the connection "address" for ALSA devices
     public static String makeAlsaAddressString(int card, int device) {
-        return "card=" + card + ";device=" + device + ";";
+        return "card=" + card + ";device=" + device;
     }
 
     public static final class Lifecycle extends SystemService {
@@ -4087,13 +4087,14 @@
                 return;
         }
 
-        // Forcefully set LE audio volume as a workaround, since in some cases
-        // (like the outgoing call) the value of 'device' is not DEVICE_OUT_BLE_*
-        // even when BLE is connected.
+        // In some cases (like the outgoing or rejected call) the value of 'device' is not
+        // DEVICE_OUT_BLE_* even when BLE is connected. Changing the volume level in such case
+        // may cuase the other devices volume level leaking into the LeAudio device settings.
         if (!AudioSystem.isLeAudioDeviceType(device)) {
-            Log.w(TAG, "setLeAudioVolumeOnModeUpdate got unexpected device=" + device
-                    + ", forcing to device=" + AudioSystem.DEVICE_OUT_BLE_HEADSET);
-            device = AudioSystem.DEVICE_OUT_BLE_HEADSET;
+            Log.w(TAG, "setLeAudioVolumeOnModeUpdate ignoring invalid device="
+                    + device + ", mode=" + mode + ", index=" + index + " maxIndex=" + maxIndex
+                    + " streamType=" + streamType);
+            return;
         }
 
         if (DEBUG_VOL) {
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index a9e7d4b..c9cdba9 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -106,12 +106,12 @@
     };
 
     // Spatializer state machine
-    private static final int STATE_UNINITIALIZED = 0;
-    private static final int STATE_NOT_SUPPORTED = 1;
-    private static final int STATE_DISABLED_UNAVAILABLE = 3;
-    private static final int STATE_ENABLED_UNAVAILABLE = 4;
-    private static final int STATE_ENABLED_AVAILABLE = 5;
-    private static final int STATE_DISABLED_AVAILABLE = 6;
+    /*package*/ static final int STATE_UNINITIALIZED = 0;
+    /*package*/ static final int STATE_NOT_SUPPORTED = 1;
+    /*package*/ static final int STATE_DISABLED_UNAVAILABLE = 3;
+    /*package*/ static final int STATE_ENABLED_UNAVAILABLE = 4;
+    /*package*/ static final int STATE_ENABLED_AVAILABLE = 5;
+    /*package*/ static final int STATE_DISABLED_AVAILABLE = 6;
     private int mState = STATE_UNINITIALIZED;
 
     private boolean mFeatureEnabled = false;
@@ -147,9 +147,9 @@
             .setSampleRate(48000)
             .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1)
             .build();
-    // device array to store the routing for the default attributes and format, size 1 because
-    // media is never expected to be duplicated
-    private static final AudioDeviceAttributes[] ROUTING_DEVICES = new AudioDeviceAttributes[1];
+    // device array to store the routing for the default attributes and format, initialized to
+    // an empty list as routing hasn't been established yet
+    private static ArrayList<AudioDeviceAttributes> sRoutingDevices = new ArrayList<>(0);
 
     //---------------------------------------------------------------
     // audio device compatibility / enabled
@@ -184,11 +184,6 @@
         SADeviceState.sHeadTrackingEnabledDefault = headTrackingEnabledDefault;
     }
 
-    synchronized void initForTest(boolean hasBinaural, boolean hasTransaural) {
-        mBinauralSupported = hasBinaural;
-        mTransauralSupported = hasTransaural;
-    }
-
     synchronized void init(boolean effectExpected, @Nullable String settings) {
         loglogi("init effectExpected=" + effectExpected);
         if (!effectExpected) {
@@ -322,8 +317,7 @@
             return;
         }
         mState = STATE_DISABLED_UNAVAILABLE;
-        mASA.getDevicesForAttributes(
-                DEFAULT_ATTRIBUTES, false /* forVolume */).toArray(ROUTING_DEVICES);
+        sRoutingDevices = getRoutingDevices(DEFAULT_ATTRIBUTES);
         // note at this point mSpat is still not instantiated
     }
 
@@ -365,34 +359,35 @@
             case STATE_DISABLED_AVAILABLE:
                 break;
         }
-        mASA.getDevicesForAttributes(
-                DEFAULT_ATTRIBUTES, false /* forVolume */).toArray(ROUTING_DEVICES);
+
+        sRoutingDevices = getRoutingDevices(DEFAULT_ATTRIBUTES);
 
         // check validity of routing information
-        if (ROUTING_DEVICES[0] == null) {
-            logloge("onRoutingUpdated: device is null, no Spatial Audio");
+        if (sRoutingDevices.isEmpty()) {
+            logloge("onRoutingUpdated: no device, no Spatial Audio");
             setDispatchAvailableState(false);
             // not changing the spatializer level as this is likely a transient state
             return;
         }
+        final AudioDeviceAttributes currentDevice = sRoutingDevices.get(0);
 
         // is media routed to a new device?
-        if (isWireless(ROUTING_DEVICES[0].getType())) {
-            addWirelessDeviceIfNew(ROUTING_DEVICES[0]);
+        if (isWireless(currentDevice.getType())) {
+            addWirelessDeviceIfNew(currentDevice);
         }
 
         // find if media device enabled / available
-        final Pair<Boolean, Boolean> enabledAvailable = evaluateState(ROUTING_DEVICES[0]);
+        final Pair<Boolean, Boolean> enabledAvailable = evaluateState(currentDevice);
 
         boolean able = false;
         if (enabledAvailable.second) {
             // available for Spatial audio, check w/ effect
-            able = canBeSpatializedOnDevice(DEFAULT_ATTRIBUTES, DEFAULT_FORMAT, ROUTING_DEVICES);
+            able = canBeSpatializedOnDevice(DEFAULT_ATTRIBUTES, DEFAULT_FORMAT, sRoutingDevices);
             loglogi("onRoutingUpdated: can spatialize media 5.1:" + able
-                    + " on device:" + ROUTING_DEVICES[0]);
+                    + " on device:" + currentDevice);
             setDispatchAvailableState(able);
         } else {
-            loglogi("onRoutingUpdated: device:" + ROUTING_DEVICES[0]
+            loglogi("onRoutingUpdated: device:" + currentDevice
                     + " not available for Spatial Audio");
             setDispatchAvailableState(false);
         }
@@ -400,10 +395,10 @@
         boolean enabled = able && enabledAvailable.first;
         if (enabled) {
             loglogi("Enabling Spatial Audio since enabled for media device:"
-                    + ROUTING_DEVICES[0]);
+                    + currentDevice);
         } else {
             loglogi("Disabling Spatial Audio since disabled for media device:"
-                    + ROUTING_DEVICES[0]);
+                    + currentDevice);
         }
         if (mSpat != null) {
             byte level = enabled ? (byte) Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL
@@ -736,9 +731,13 @@
     }
 
     private synchronized boolean canBeSpatializedOnDevice(@NonNull AudioAttributes attributes,
-            @NonNull AudioFormat format, @NonNull AudioDeviceAttributes[] devices) {
-        if (isDeviceCompatibleWithSpatializationModes(devices[0])) {
-            return AudioSystem.canBeSpatialized(attributes, format, devices);
+            @NonNull AudioFormat format, @NonNull ArrayList<AudioDeviceAttributes> devices) {
+        if (devices.isEmpty()) {
+            return false;
+        }
+        if (isDeviceCompatibleWithSpatializationModes(devices.get(0))) {
+            AudioDeviceAttributes[] devArray = new AudioDeviceAttributes[devices.size()];
+            return AudioSystem.canBeSpatialized(attributes, format, devices.toArray(devArray));
         }
         return false;
     }
@@ -1014,10 +1013,13 @@
                 logd("canBeSpatialized false due to usage:" + attributes.getUsage());
                 return false;
         }
-        AudioDeviceAttributes[] devices = new AudioDeviceAttributes[1];
+
         // going through adapter to take advantage of routing cache
-        mASA.getDevicesForAttributes(
-                attributes, false /* forVolume */).toArray(devices);
+        final ArrayList<AudioDeviceAttributes> devices = getRoutingDevices(attributes);
+        if (devices.isEmpty()) {
+            logloge("canBeSpatialized got no device for " + attributes);
+            return false;
+        }
         final boolean able = canBeSpatializedOnDevice(attributes, format, devices);
         logd("canBeSpatialized usage:" + attributes.getUsage()
                 + " format:" + format.toLogFriendlyString() + " returning " + able);
@@ -1148,8 +1150,13 @@
         logDeviceState(deviceState, "setHeadTrackerEnabled");
 
         // check current routing to see if it affects the headtracking mode
-        if (ROUTING_DEVICES[0] != null && ROUTING_DEVICES[0].getType() == ada.getType()
-                && ROUTING_DEVICES[0].getAddress().equals(ada.getAddress())) {
+        if (sRoutingDevices.isEmpty()) {
+            logloge("setHeadTrackerEnabled: no device, bailing");
+            return;
+        }
+        final AudioDeviceAttributes currentDevice = sRoutingDevices.get(0);
+        if (currentDevice.getType() == ada.getType()
+                && currentDevice.getAddress().equals(ada.getAddress())) {
             setDesiredHeadTrackingMode(enabled ? mDesiredHeadTrackingModeWhenEnabled
                     : Spatializer.HEAD_TRACKING_MODE_DISABLED);
             if (enabled && !mHeadTrackerAvailable) {
@@ -1706,10 +1713,11 @@
 
     private int getHeadSensorHandleUpdateTracker() {
         int headHandle = -1;
-        final AudioDeviceAttributes currentDevice = ROUTING_DEVICES[0];
-        if (currentDevice == null) {
+        if (sRoutingDevices.isEmpty()) {
+            logloge("getHeadSensorHandleUpdateTracker: no device, no head tracker");
             return headHandle;
         }
+        final AudioDeviceAttributes currentDevice = sRoutingDevices.get(0);
         UUID routingDeviceUuid = mAudioService.getDeviceSensorUuid(currentDevice);
         // We limit only to Sensor.TYPE_HEAD_TRACKER here to avoid confusion
         // with gaming sensors. (Note that Sensor.TYPE_ROTATION_VECTOR
@@ -1743,6 +1751,23 @@
         return screenHandle;
     }
 
+    /**
+     * Returns routing for the given attributes
+     * @param aa AudioAttributes whose routing is being queried
+     * @return a non-null never-empty list of devices. If the routing query failed, the list
+     *     will contain null.
+     */
+    private @NonNull ArrayList<AudioDeviceAttributes> getRoutingDevices(AudioAttributes aa) {
+        final ArrayList<AudioDeviceAttributes> devices = mASA.getDevicesForAttributes(
+                aa, false /* forVolume */);
+        for (AudioDeviceAttributes ada : devices) {
+            if (ada == null) {
+                // invalid entry, reject this routing query by returning an empty list
+                return new ArrayList<>(0);
+            }
+        }
+        return devices;
+    }
 
     private static void loglogi(String msg) {
         AudioService.sSpatialLogger.loglogi(msg, TAG);
@@ -1759,4 +1784,13 @@
     /*package*/ void clearSADevices() {
         mSADevices.clear();
     }
+
+    /*package*/ synchronized void forceStateForTest(int state) {
+        mState = state;
+    }
+
+    /*package*/ synchronized void initForTest(boolean hasBinaural, boolean hasTransaural) {
+        mBinauralSupported = hasBinaural;
+        mTransauralSupported = hasTransaural;
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 1e9352d1..b25206d 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -271,6 +271,13 @@
     static final int DEFAULT_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT = 5 * 60;
 
     /**
+     * Default keepalive value to consider long-lived TCP connections are expensive on the
+     * VPN network from battery usage point of view.
+     * TODO: consider reading from setting.
+     */
+    @VisibleForTesting
+    static final int DEFAULT_LONG_LIVED_TCP_CONNS_EXPENSIVE_TIMEOUT_SEC = 60;
+    /**
      *  Prefer using {@link IkeSessionParams.ESP_IP_VERSION_AUTO} and
      *  {@link IkeSessionParams.ESP_ENCAP_TYPE_AUTO} for ESP packets.
      *
@@ -358,9 +365,8 @@
         return mVpnProfileStore;
     }
 
-    private static final int MAX_EVENTS_LOGS = 20;
-    private final LocalLog mUnderlyNetworkChanges = new LocalLog(MAX_EVENTS_LOGS);
-    private final LocalLog mVpnManagerEvents = new LocalLog(MAX_EVENTS_LOGS);
+    private static final int MAX_EVENTS_LOGS = 100;
+    private final LocalLog mEventChanges = new LocalLog(MAX_EVENTS_LOGS);
 
     /**
      * Cached Map of <subscription ID, CarrierConfigInfo> since retrieving the PersistableBundle
@@ -950,7 +956,7 @@
             int errorCode, @NonNull final String packageName, @Nullable final String sessionKey,
             @NonNull final VpnProfileState profileState, @Nullable final Network underlyingNetwork,
             @Nullable final NetworkCapabilities nc, @Nullable final LinkProperties lp) {
-        mVpnManagerEvents.log("Event class=" + getVpnManagerEventClassName(errorClass)
+        mEventChanges.log("[VMEvent] Event class=" + getVpnManagerEventClassName(errorClass)
                 + ", err=" + getVpnManagerEventErrorName(errorCode) + " for " + packageName
                 + " on session " + sessionKey);
         final Intent intent = buildVpnManagerEventIntent(category, errorClass, errorCode,
@@ -1100,6 +1106,8 @@
         mLockdownAllowlist = (mLockdown && lockdownAllowlist != null)
                 ? Collections.unmodifiableList(new ArrayList<>(lockdownAllowlist))
                 : Collections.emptyList();
+        mEventChanges.log("[LockdownAlwaysOn] Mode changed: lockdown=" + mLockdown + " alwaysOn="
+                + mAlwaysOn + " calling from " + Binder.getCallingUid());
 
         if (isCurrentPreparedPackage(packageName)) {
             updateAlwaysOnNotification(mNetworkInfo.getDetailedState());
@@ -1670,9 +1678,12 @@
         capsBuilder.setUids(createUserAndRestrictedProfilesRanges(mUserId,
                 mConfig.allowedApplications, mConfig.disallowedApplications));
 
-        capsBuilder.setTransportInfo(
-                new VpnTransportInfo(getActiveVpnType(), mConfig.session, mConfig.allowBypass,
-                        false /* longLivedTcpConnectionsExpensive */));
+        final boolean expensive = areLongLivedTcpConnectionsExpensive(mVpnRunner);
+        capsBuilder.setTransportInfo(new VpnTransportInfo(
+                getActiveVpnType(),
+                mConfig.session,
+                mConfig.allowBypass,
+                expensive));
 
         // Only apps targeting Q and above can explicitly declare themselves as metered.
         // These VPNs are assumed metered unless they state otherwise.
@@ -1704,6 +1715,17 @@
         updateState(DetailedState.CONNECTED, "agentConnect");
     }
 
+    private static boolean areLongLivedTcpConnectionsExpensive(@NonNull VpnRunner runner) {
+        if (!(runner instanceof IkeV2VpnRunner)) return false;
+
+        final int delay = ((IkeV2VpnRunner) runner).getOrGuessKeepaliveDelaySeconds();
+        return areLongLivedTcpConnectionsExpensive(delay);
+    }
+
+    private static boolean areLongLivedTcpConnectionsExpensive(int keepaliveDelaySec) {
+        return keepaliveDelaySec < DEFAULT_LONG_LIVED_TCP_CONNS_EXPENSIVE_TIMEOUT_SEC;
+    }
+
     private boolean canHaveRestrictedProfile(int userId) {
         final long token = Binder.clearCallingIdentity();
         try {
@@ -1715,7 +1737,7 @@
     }
 
     private void logUnderlyNetworkChanges(List<Network> networks) {
-        mUnderlyNetworkChanges.log("Switch to "
+        mEventChanges.log("[UnderlyingNW] Switch to "
                 + ((networks != null) ? TextUtils.join(", ", networks) : "null"));
     }
 
@@ -2982,16 +3004,17 @@
                     @Override
                     public void onCarrierConfigChanged(int slotIndex, int subId, int carrierId,
                             int specificCarrierId) {
+                        mEventChanges.log("[CarrierConfig] Changed on slot " + slotIndex + " subId="
+                                + subId + " carrerId=" + carrierId
+                                + " specificCarrierId=" + specificCarrierId);
                         synchronized (Vpn.this) {
                             mCachedCarrierConfigInfoPerSubId.remove(subId);
 
                             // Ignore stale runner.
                             if (mVpnRunner != Vpn.IkeV2VpnRunner.this) return;
 
-                            maybeMigrateIkeSession(mActiveNetwork);
+                            maybeMigrateIkeSessionAndUpdateVpnTransportInfo(mActiveNetwork);
                         }
-                        // TODO: update the longLivedTcpConnectionsExpensive value in the
-                        //  networkcapabilities of the VPN network.
                     }
         };
 
@@ -3074,6 +3097,8 @@
          */
         public void onIkeOpened(int token, @NonNull IkeSessionConfiguration ikeConfiguration) {
             if (!isActiveToken(token)) {
+                mEventChanges.log("[IKEEvent-" + mSessionKey + "] onIkeOpened obsolete token="
+                        + token);
                 Log.d(TAG, "onIkeOpened called for obsolete token " + token);
                 return;
             }
@@ -3081,7 +3106,12 @@
             mMobikeEnabled =
                     ikeConfiguration.isIkeExtensionEnabled(
                             IkeSessionConfiguration.EXTENSION_TYPE_MOBIKE);
-            onIkeConnectionInfoChanged(token, ikeConfiguration.getIkeSessionConnectionInfo());
+            final IkeSessionConnectionInfo info = ikeConfiguration.getIkeSessionConnectionInfo();
+            mEventChanges.log("[IKEEvent-" + mSessionKey + "] onIkeOpened token=" + token
+                    + ", localAddr=" + info.getLocalAddress()
+                    + ", network=" + info.getNetwork()
+                    + ", mobikeEnabled= " + mMobikeEnabled);
+            onIkeConnectionInfoChanged(token, info);
         }
 
         /**
@@ -3094,11 +3124,17 @@
          */
         public void onIkeConnectionInfoChanged(
                 int token, @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
+
             if (!isActiveToken(token)) {
+                mEventChanges.log("[IKEEvent-" + mSessionKey
+                        + "] onIkeConnectionInfoChanged obsolete token=" + token);
                 Log.d(TAG, "onIkeConnectionInfoChanged called for obsolete token " + token);
                 return;
             }
-
+            mEventChanges.log("[IKEEvent-" + mSessionKey
+                    + "] onIkeConnectionInfoChanged token=" + token
+                    + ", localAddr=" + ikeConnectionInfo.getLocalAddress()
+                    + ", network=" + ikeConnectionInfo.getNetwork());
             // The update on VPN and the IPsec tunnel will be done when migration is fully complete
             // in onChildMigrated
             mIkeConnectionInfo = ikeConnectionInfo;
@@ -3112,6 +3148,8 @@
          */
         public void onChildOpened(int token, @NonNull ChildSessionConfiguration childConfig) {
             if (!isActiveToken(token)) {
+                mEventChanges.log("[IKEEvent-" + mSessionKey
+                        + "] onChildOpened obsolete token=" + token);
                 Log.d(TAG, "onChildOpened called for obsolete token " + token);
 
                 // Do nothing; this signals that either: (1) a new/better Network was found,
@@ -3121,7 +3159,9 @@
                 // sessions are torn down via resetIkeState().
                 return;
             }
-
+            mEventChanges.log("[IKEEvent-" + mSessionKey + "] onChildOpened token=" + token
+                    + ", addr=" + TextUtils.join(", ", childConfig.getInternalAddresses())
+                    + " dns=" + TextUtils.join(", ", childConfig.getInternalDnsServers()));
             try {
                 final String interfaceName = mTunnelIface.getInterfaceName();
                 final List<LinkAddress> internalAddresses = childConfig.getInternalAddresses();
@@ -3218,6 +3258,8 @@
         public void onChildTransformCreated(
                 int token, @NonNull IpSecTransform transform, int direction) {
             if (!isActiveToken(token)) {
+                mEventChanges.log("[IKEEvent-" + mSessionKey
+                        + "] onChildTransformCreated obsolete token=" + token);
                 Log.d(TAG, "ChildTransformCreated for obsolete token " + token);
 
                 // Do nothing; this signals that either: (1) a new/better Network was found,
@@ -3227,7 +3269,9 @@
                 // sessions are torn down via resetIkeState().
                 return;
             }
-
+            mEventChanges.log("[IKEEvent-" + mSessionKey
+                    + "] onChildTransformCreated token=" + token + ", direction=" + direction
+                    + ", transform=" + transform);
             try {
                 mTunnelIface.setUnderlyingNetwork(mIkeConnectionInfo.getNetwork());
 
@@ -3252,10 +3296,14 @@
                 @NonNull IpSecTransform inTransform,
                 @NonNull IpSecTransform outTransform) {
             if (!isActiveToken(token)) {
+                mEventChanges.log("[IKEEvent-" + mSessionKey
+                        + "] onChildMigrated obsolete token=" + token);
                 Log.d(TAG, "onChildMigrated for obsolete token " + token);
                 return;
             }
-
+            mEventChanges.log("[IKEEvent-" + mSessionKey
+                    + "] onChildMigrated token=" + token
+                    + ", in=" + inTransform + ", out=" + outTransform);
             // The actual network of this IKE session has migrated to is
             // mIkeConnectionInfo.getNetwork() instead of mActiveNetwork because mActiveNetwork
             // might have been updated after the migration was triggered.
@@ -3442,7 +3490,7 @@
                 return;
             }
 
-            if (maybeMigrateIkeSession(underlyingNetwork)) return;
+            if (maybeMigrateIkeSessionAndUpdateVpnTransportInfo(underlyingNetwork)) return;
 
             startIkeSession(underlyingNetwork);
         }
@@ -3549,7 +3597,43 @@
             return new CarrierConfigInfo(mccMnc, natKeepalive, encapType, ipVersion);
         }
 
-        boolean maybeMigrateIkeSession(@NonNull Network underlyingNetwork) {
+        private int getOrGuessKeepaliveDelaySeconds() {
+            if (mProfile.isAutomaticNattKeepaliveTimerEnabled()) {
+                return guessNattKeepaliveTimerForNetwork();
+            } else if (mProfile.getIkeTunnelConnectionParams() != null) {
+                return mProfile.getIkeTunnelConnectionParams()
+                        .getIkeSessionParams().getNattKeepAliveDelaySeconds();
+            }
+            return DEFAULT_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT;
+        }
+
+        boolean maybeMigrateIkeSessionAndUpdateVpnTransportInfo(
+                @NonNull Network underlyingNetwork) {
+            final int keepaliveDelaySec = getOrGuessKeepaliveDelaySeconds();
+            final boolean migrated = maybeMigrateIkeSession(underlyingNetwork, keepaliveDelaySec);
+            if (migrated) {
+                updateVpnTransportInfoAndNetCap(keepaliveDelaySec);
+            }
+            return migrated;
+        }
+
+        public void updateVpnTransportInfoAndNetCap(int keepaliveDelaySec) {
+            final VpnTransportInfo info = new VpnTransportInfo(
+                    getActiveVpnType(),
+                    mConfig.session,
+                    mConfig.allowBypass,
+                    areLongLivedTcpConnectionsExpensive(keepaliveDelaySec));
+            final boolean ncUpdateRequired = !info.equals(mNetworkCapabilities.getTransportInfo());
+            if (ncUpdateRequired) {
+                mNetworkCapabilities = new NetworkCapabilities.Builder(mNetworkCapabilities)
+                        .setTransportInfo(info)
+                        .build();
+                doSendNetworkCapabilities(mNetworkAgent, mNetworkCapabilities);
+            }
+        }
+
+        private boolean maybeMigrateIkeSession(@NonNull Network underlyingNetwork,
+                int keepaliveDelaySeconds) {
             if (mSession == null || !mMobikeEnabled) return false;
 
             // IKE session can schedule a migration event only when IKE AUTH is finished
@@ -3574,15 +3658,6 @@
                 encapType = ESP_ENCAP_TYPE_AUTO;
             }
 
-            final int keepaliveDelaySeconds;
-            if (mProfile.isAutomaticNattKeepaliveTimerEnabled()) {
-                keepaliveDelaySeconds = guessNattKeepaliveTimerForNetwork();
-            } else if (mProfile.getIkeTunnelConnectionParams() != null) {
-                keepaliveDelaySeconds = mProfile.getIkeTunnelConnectionParams()
-                        .getIkeSessionParams().getNattKeepAliveDelaySeconds();
-            } else {
-                keepaliveDelaySeconds = DEFAULT_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT;
-            }
             mSession.setNetwork(underlyingNetwork, ipVersion, encapType, keepaliveDelaySeconds);
             return true;
         }
@@ -3656,6 +3731,8 @@
 
         /** Called when the NetworkCapabilities of underlying network is changed */
         public void onDefaultNetworkCapabilitiesChanged(@NonNull NetworkCapabilities nc) {
+            mEventChanges.log("[UnderlyingNW] Cap changed from "
+                    + mUnderlyingNetworkCapabilities + " to " + nc);
             final NetworkCapabilities oldNc = mUnderlyingNetworkCapabilities;
             mUnderlyingNetworkCapabilities = nc;
             if (oldNc == null) {
@@ -3663,12 +3740,14 @@
                 startOrMigrateIkeSession(mActiveNetwork);
             } else if (!nc.getSubscriptionIds().equals(oldNc.getSubscriptionIds())) {
                 // Renew carrierConfig values.
-                maybeMigrateIkeSession(mActiveNetwork);
+                maybeMigrateIkeSessionAndUpdateVpnTransportInfo(mActiveNetwork);
             }
         }
 
         /** Called when the LinkProperties of underlying network is changed */
         public void onDefaultNetworkLinkPropertiesChanged(@NonNull LinkProperties lp) {
+            mEventChanges.log("[UnderlyingNW] Lp changed from "
+                    + mUnderlyingLinkProperties + " to " + lp);
             mUnderlyingLinkProperties = lp;
         }
 
@@ -3691,7 +3770,7 @@
                         Log.d(TAG, "Data stall suspected");
 
                         // Trigger MOBIKE.
-                        maybeMigrateIkeSession(mActiveNetwork);
+                        maybeMigrateIkeSessionAndUpdateVpnTransportInfo(mActiveNetwork);
                         mDataStallSuspected = true;
                     }
                 }
@@ -4673,7 +4752,7 @@
         // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
         //  ConnectivityServiceTest.
         if (SdkLevel.isAtLeastT()) {
-            mVpnManagerEvents.log(packageName + " stopped");
+            mEventChanges.log("[VMEvent] " + packageName + " stopped");
             sendEventToVpnManagerApp(intent, packageName);
         }
     }
@@ -5007,23 +5086,21 @@
             pw.println("NetworkCapabilities: " + mNetworkCapabilities);
             if (isIkev2VpnRunner()) {
                 final IkeV2VpnRunner runner = ((IkeV2VpnRunner) mVpnRunner);
-                pw.println("Token: " + runner.mSessionKey);
+                pw.println("SessionKey: " + runner.mSessionKey);
                 pw.println("MOBIKE " + (runner.mMobikeEnabled ? "enabled" : "disabled"));
+                pw.println("Profile: " + runner.mProfile);
+                pw.println("Token: " + runner.mCurrentToken);
                 if (mDataStallSuspected) pw.println("Data stall suspected");
                 if (runner.mScheduledHandleDataStallFuture != null) {
                     pw.println("Reset session scheduled");
                 }
             }
+            pw.println();
             pw.println("mCachedCarrierConfigInfoPerSubId=" + mCachedCarrierConfigInfoPerSubId);
 
-            pw.println("mUnderlyNetworkChanges (most recent first):");
+            pw.println("mEventChanges (most recent first):");
             pw.increaseIndent();
-            mUnderlyNetworkChanges.reverseDump(pw);
-            pw.decreaseIndent();
-
-            pw.println("mVpnManagerEvent (most recent first):");
-            pw.increaseIndent();
-            mVpnManagerEvents.reverseDump(pw);
+            mEventChanges.reverseDump(pw);
             pw.decreaseIndent();
         }
     }
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/OWNERS b/services/core/java/com/android/server/soundtrigger_middleware/OWNERS
index 01b2cb9..1e41886 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/OWNERS
+++ b/services/core/java/com/android/server/soundtrigger_middleware/OWNERS
@@ -1,2 +1 @@
-atneya@google.com
-elaurent@google.com
+include /media/java/android/media/soundtrigger/OWNERS
diff --git a/services/core/java/com/android/server/vcn/TEST_MAPPING b/services/core/java/com/android/server/vcn/TEST_MAPPING
new file mode 100644
index 0000000..5b04d88
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksVcnTests"
+    },
+    {
+      "name": "CtsVcnTestCases"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index 2141eba..7f129ea 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -171,6 +171,18 @@
             return false;
         }
 
+        for (Map.Entry<Integer, Integer> entry :
+                networkPriority.getCapabilitiesMatchCriteria().entrySet()) {
+            final int cap = entry.getKey();
+            final int matchCriteria = entry.getValue();
+
+            if (matchCriteria == MATCH_REQUIRED && !caps.hasCapability(cap)) {
+                return false;
+            } else if (matchCriteria == MATCH_FORBIDDEN && caps.hasCapability(cap)) {
+                return false;
+            }
+        }
+
         if (vcnContext.isInTestMode() && caps.hasTransport(TRANSPORT_TEST)) {
             return true;
         }
@@ -319,18 +331,6 @@
             return false;
         }
 
-        for (Map.Entry<Integer, Integer> entry :
-                networkPriority.getCapabilitiesMatchCriteria().entrySet()) {
-            final int cap = entry.getKey();
-            final int matchCriteria = entry.getValue();
-
-            if (matchCriteria == MATCH_REQUIRED && !caps.hasCapability(cap)) {
-                return false;
-            } else if (matchCriteria == MATCH_FORBIDDEN && caps.hasCapability(cap)) {
-                return false;
-            }
-        }
-
         return true;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
index dea31d7..3ad24de 100644
--- a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
@@ -17,12 +17,17 @@
 
 import com.android.server.audio.SpatializerHelper.SADeviceState;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
 
+import android.media.AudioAttributes;
 import android.media.AudioDeviceAttributes;
 import android.media.AudioDeviceInfo;
+import android.media.AudioFormat;
 import android.media.AudioSystem;
 import android.util.Log;
 
@@ -36,6 +41,7 @@
 import org.mockito.Mock;
 import org.mockito.Spy;
 
+import java.util.ArrayList;
 import java.util.List;
 
 @MediumTest
@@ -49,16 +55,35 @@
 
     @Mock private AudioService mMockAudioService;
     @Spy private AudioSystemAdapter mSpyAudioSystem;
+    @Mock private AudioSystemAdapter mMockAudioSystem;
 
     @Before
     public void setUp() throws Exception {
         mMockAudioService = mock(AudioService.class);
-        mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
+    }
 
-        mSpatHelper = new SpatializerHelper(mMockAudioService, mSpyAudioSystem,
+    /**
+     * Initializes mSpatHelper, the SpatizerHelper instance under test, to use the mock or spy
+     * AudioSystemAdapter
+     * @param useSpyAudioSystem true to use the spy adapter, mSpyAudioSystem, or false to use
+     *                          the mock adapter, mMockAudioSystem.
+     */
+    private void setUpSpatHelper(boolean useSpyAudioSystem) {
+        final AudioSystemAdapter asAdapter;
+        if (useSpyAudioSystem) {
+            mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
+            asAdapter = mSpyAudioSystem;
+            mMockAudioSystem = null;
+        } else {
+            mSpyAudioSystem = null;
+            mMockAudioSystem = mock(NoOpAudioSystemAdapter.class);
+            asAdapter = mMockAudioSystem;
+        }
+        mSpatHelper = new SpatializerHelper(mMockAudioService, asAdapter,
                 true /*binauralEnabledDefault*/,
                 true /*transauralEnabledDefault*/,
                 false /*headTrackingEnabledDefault*/);
+
     }
 
     /**
@@ -68,6 +93,7 @@
      */
     @Test
     public void testSADeviceStateNullAddressCtor() throws Exception {
+        setUpSpatHelper(true /*useSpyAudioSystem*/);
         try {
             SADeviceState devState = new SADeviceState(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, null);
             devState = new SADeviceState(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, null);
@@ -78,6 +104,7 @@
     @Test
     public void testSADeviceStateStringSerialization() throws Exception {
         Log.i(TAG, "starting testSADeviceStateStringSerialization");
+        setUpSpatHelper(true /*useSpyAudioSystem*/);
         final SADeviceState devState = new SADeviceState(
                 AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, "bla");
         devState.mHasHeadTracker = false;
@@ -93,6 +120,7 @@
     @Test
     public void testSADeviceSettings() throws Exception {
         Log.i(TAG, "starting testSADeviceSettings");
+        setUpSpatHelper(true /*useSpyAudioSystem*/);
         final AudioDeviceAttributes dev1 =
                 new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, "");
         final AudioDeviceAttributes dev2 =
@@ -143,4 +171,34 @@
         Log.i(TAG, "device settingsRestored: " + settingsRestored);
         Assert.assertEquals(settings, settingsRestored);
     }
+
+    /**
+     * Test that null devices for routing do not break canBeSpatialized
+     * @throws Exception
+     */
+    @Test
+    public void testNoRoutingCanBeSpatialized() throws Exception {
+        Log.i(TAG, "Starting testNoRoutingCanBeSpatialized");
+        setUpSpatHelper(false /*useSpyAudioSystem*/);
+        mSpatHelper.forceStateForTest(SpatializerHelper.STATE_ENABLED_AVAILABLE);
+
+        final ArrayList<AudioDeviceAttributes> emptyList = new ArrayList<>(0);
+        final ArrayList<AudioDeviceAttributes> listWithNull = new ArrayList<>(1);
+        listWithNull.add(null);
+        final AudioAttributes media = new AudioAttributes.Builder()
+                .setUsage(AudioAttributes.USAGE_MEDIA).build();
+        final AudioFormat spatialFormat = new AudioFormat.Builder()
+                .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1).build();
+
+        when(mMockAudioSystem.getDevicesForAttributes(any(AudioAttributes.class), anyBoolean()))
+                .thenReturn(emptyList);
+        Assert.assertFalse("can be spatialized on empty routing",
+                mSpatHelper.canBeSpatialized(media, spatialFormat));
+
+        when(mMockAudioSystem.getDevicesForAttributes(any(AudioAttributes.class), anyBoolean()))
+                .thenReturn(listWithNull);
+        Assert.assertFalse("can be spatialized on null routing",
+                mSpatHelper.canBeSpatialized(media, spatialFormat));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS
index 33385af..1e41886 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS
@@ -1 +1 @@
-include /media/aidl/android/media/soundtrigger_middleware/OWNERS
+include /media/java/android/media/soundtrigger/OWNERS
diff --git a/services/voiceinteraction/OWNERS b/services/voiceinteraction/OWNERS
index ef1061b..40e8d26 100644
--- a/services/voiceinteraction/OWNERS
+++ b/services/voiceinteraction/OWNERS
@@ -1 +1,2 @@
 include /core/java/android/service/voice/OWNERS
+include /media/java/android/media/soundtrigger/OWNERS
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS b/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS
index 01b2cb9..1e41886 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/OWNERS
@@ -1,2 +1 @@
-atneya@google.com
-elaurent@google.com
+include /media/java/android/media/soundtrigger/OWNERS
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index f570f87..17780af 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4478,6 +4478,57 @@
             "data_stall_recovery_should_skip_bool_array";
 
     /**
+     * String array containing the list of names for service numbers provided by carriers. This key
+     * should be used with {@link #KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY}. The names provided in
+     * this array will be mapped 1:1 with the numbers provided in the {@link
+     * #KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY} array.
+     *
+     * <p>The data would be considered valid if and only if:
+     *
+     * <ul>
+     *   <li>The number of items in both the arrays are equal
+     *   <li>The data added to the {@link #KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY} array is valid.
+     *       See {@link #KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY} for more information.
+     * </ul>
+     *
+     * <p>Example:
+     *
+     * <pre><code>
+     * <string-array name="carrier_service_name_array" num="2">
+     *   <item value="Police"/>
+     *   <item value="Ambulance"/>
+     * </string-array>
+     * </code></pre>
+     */
+    public static final String KEY_CARRIER_SERVICE_NAME_STRING_ARRAY = "carrier_service_name_array";
+
+    /**
+     * String array containing the list of service numbers provided by carriers. This key should be
+     * used with {@link #KEY_CARRIER_SERVICE_NAME_STRING_ARRAY}. The numbers provided in this array
+     * will be mapped 1:1 with the names provided in the {@link
+     * #KEY_CARRIER_SERVICE_NAME_STRING_ARRAY} array.
+     *
+     * <p>The data would be considered valid if and only if:
+     *
+     * <ul>
+     *   <li>The number of items in both the arrays are equal
+     *   <li>The item added in this key follows a specific format. Either it should be all numbers,
+     *       or "+" followed by all numbers.
+     * </ul>
+     *
+     * <p>Example:
+     *
+     * <pre><code>
+     * <string-array name="carrier_service_number_array" num="2">
+     *   <item value="123"/>
+     *   <item value="+343"/>
+     * </string-array>
+     * </code></pre>
+     */
+    public static final String KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY =
+        "carrier_service_number_array";
+
+    /**
      * Configs used by ImsServiceEntitlement.
      */
     public static final class ImsServiceEntitlement {
@@ -9350,6 +9401,8 @@
                 new long[] {180000, 180000, 180000, 180000});
         sDefaults.putBooleanArray(KEY_DATA_STALL_RECOVERY_SHOULD_SKIP_BOOL_ARRAY,
                 new boolean[] {false, false, true, false, false});
+        sDefaults.putStringArray(KEY_CARRIER_SERVICE_NAME_STRING_ARRAY, new String[0]);
+        sDefaults.putStringArray(KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY, new String[0]);
     }
 
     /**
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 1d6798b..f1f13bc 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -184,10 +184,11 @@
     private final int mTransportType;
 
     /**
-     * The initial registration state
+     * The true registration state of network, This is not affected by any carrier config or
+     * resource overlay.
      */
     @RegistrationState
-    private final int mInitialRegistrationState;
+    private final int mNetworkRegistrationState;
 
     /**
      * The registration state that might have been overridden by config
@@ -264,7 +265,7 @@
         mDomain = domain;
         mTransportType = transportType;
         mRegistrationState = registrationState;
-        mInitialRegistrationState = registrationState;
+        mNetworkRegistrationState = registrationState;
         mRoamingType = (registrationState == REGISTRATION_STATE_ROAMING)
                 ? ServiceState.ROAMING_TYPE_UNKNOWN : ServiceState.ROAMING_TYPE_NOT_ROAMING;
         setAccessNetworkTechnology(accessNetworkTechnology);
@@ -320,7 +321,7 @@
         mDomain = source.readInt();
         mTransportType = source.readInt();
         mRegistrationState = source.readInt();
-        mInitialRegistrationState = source.readInt();
+        mNetworkRegistrationState = source.readInt();
         mRoamingType = source.readInt();
         mAccessNetworkTechnology = source.readInt();
         mRejectCause = source.readInt();
@@ -347,7 +348,7 @@
         mDomain = nri.mDomain;
         mTransportType = nri.mTransportType;
         mRegistrationState = nri.mRegistrationState;
-        mInitialRegistrationState = nri.mInitialRegistrationState;
+        mNetworkRegistrationState = nri.mNetworkRegistrationState;
         mRoamingType = nri.mRoamingType;
         mAccessNetworkTechnology = nri.mAccessNetworkTechnology;
         mIsUsingCarrierAggregation = nri.mIsUsingCarrierAggregation;
@@ -400,40 +401,72 @@
     }
 
     /**
-     * @return The registration state.
+     * @return The registration state. Note this value can be affected by the carrier config
+     * override.
      *
+     * @deprecated Use {@link #getNetworkRegistrationState}, which is not affected by any carrier
+     * config or resource overlay, instead.
      * @hide
      */
+    @Deprecated
     @SystemApi
     public @RegistrationState int getRegistrationState() {
         return mRegistrationState;
     }
 
     /**
-     * @return The initial registration state.
+     * @return The true registration state of network. (This value is not affected by any carrier
+     * config or resource overlay override).
      *
      * @hide
      */
-    public @RegistrationState int getInitialRegistrationState() {
-        return mInitialRegistrationState;
+    @SystemApi
+    public @RegistrationState int getNetworkRegistrationState() {
+        return mNetworkRegistrationState;
     }
 
     /**
-     * @return {@code true} if registered on roaming or home network, {@code false} otherwise.
+     * @return {@code true} if registered on roaming or home network. Note this value can be
+     * affected by the carrier config override.
+     *
+     * @deprecated Use {@link #isNetworkRegistered}, which is not affected by any carrier config or
+     * resource overlay, instead.
      */
+    @Deprecated
     public boolean isRegistered() {
         return mRegistrationState == REGISTRATION_STATE_HOME
                 || mRegistrationState == REGISTRATION_STATE_ROAMING;
     }
 
     /**
-     * @return {@code true} if searching for service, {@code false} otherwise.
+     * @return {@code true} if registered on roaming or home network, {@code false} otherwise. (This
+     * value is not affected by any carrier config or resource overlay override).
      */
+    public boolean isNetworkRegistered() {
+        return mNetworkRegistrationState == REGISTRATION_STATE_HOME
+                || mNetworkRegistrationState == REGISTRATION_STATE_ROAMING;
+    }
+
+    /**
+     * @return {@code true} if searching for service, {@code false} otherwise.
+     *
+     * @deprecated Use {@link #isNetworkRegistered}, which is not affected by any carrier config or
+     * resource overlay, instead.
+     */
+    @Deprecated
     public boolean isSearching() {
         return mRegistrationState == REGISTRATION_STATE_NOT_REGISTERED_SEARCHING;
     }
 
     /**
+     * @return {@code true} if searching for service, {@code false} otherwise. (This value is not
+     * affected by any carrier config or resource overlay override).
+     */
+    public boolean isNetworkSearching() {
+        return mNetworkRegistrationState == REGISTRATION_STATE_NOT_REGISTERED_SEARCHING;
+    }
+
+    /**
      * Get the PLMN-ID for this Network Registration, also known as the RPLMN.
      *
      * <p>If the device is registered, this will return the registered PLMN-ID. If registration
@@ -450,13 +483,25 @@
     }
 
     /**
-     * @return {@code true} if registered on roaming network, {@code false} otherwise.
+     * @return {@code true} if registered on roaming network overridden by config. Note this value
+     * can be affected by the carrier config override.
+     *
+     * @deprecated Use {@link TelephonyDisplayInfo#isRoaming} instead.
      */
+    @Deprecated
     public boolean isRoaming() {
         return mRoamingType != ServiceState.ROAMING_TYPE_NOT_ROAMING;
     }
 
     /**
+     * @return {@code true} if registered on roaming network. (This value is not affected by any
+     * carrier config or resource overlay override).
+     */
+    public boolean isNetworkRoaming() {
+        return mNetworkRegistrationState == REGISTRATION_STATE_ROAMING;
+    }
+
+    /**
      * @hide
      * @return {@code true} if in service.
      */
@@ -486,7 +531,8 @@
     }
 
     /**
-     * @return the current network roaming type.
+     * @return the current network roaming type. Note that this value can be possibly overridden by
+     * the carrier config or resource overlay.
      * @hide
      */
     @SystemApi
@@ -666,8 +712,8 @@
                 .append(" transportType=").append(
                         AccessNetworkConstants.transportTypeToString(mTransportType))
                 .append(" registrationState=").append(registrationStateToString(mRegistrationState))
-                .append(" mInitialRegistrationState=")
-                .append(registrationStateToString(mInitialRegistrationState))
+                .append(" networkRegistrationState=")
+                .append(registrationStateToString(mNetworkRegistrationState))
                 .append(" roamingType=").append(ServiceState.roamingTypeToString(mRoamingType))
                 .append(" accessNetworkTechnology=")
                 .append(TelephonyManager.getNetworkTypeName(mAccessNetworkTechnology))
@@ -688,7 +734,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mDomain, mTransportType, mRegistrationState, mInitialRegistrationState,
+        return Objects.hash(mDomain, mTransportType, mRegistrationState, mNetworkRegistrationState,
                 mRoamingType, mAccessNetworkTechnology, mRejectCause, mEmergencyOnly,
                 mAvailableServices, mCellIdentity, mVoiceSpecificInfo, mDataSpecificInfo, mNrState,
                 mRplmn, mIsUsingCarrierAggregation);
@@ -706,7 +752,7 @@
         return mDomain == other.mDomain
                 && mTransportType == other.mTransportType
                 && mRegistrationState == other.mRegistrationState
-                && mInitialRegistrationState == other.mInitialRegistrationState
+                && mNetworkRegistrationState == other.mNetworkRegistrationState
                 && mRoamingType == other.mRoamingType
                 && mAccessNetworkTechnology == other.mAccessNetworkTechnology
                 && mRejectCause == other.mRejectCause
@@ -729,7 +775,7 @@
         dest.writeInt(mDomain);
         dest.writeInt(mTransportType);
         dest.writeInt(mRegistrationState);
-        dest.writeInt(mInitialRegistrationState);
+        dest.writeInt(mNetworkRegistrationState);
         dest.writeInt(mRoamingType);
         dest.writeInt(mAccessNetworkTechnology);
         dest.writeInt(mRejectCause);
@@ -826,7 +872,7 @@
         private int mTransportType;
 
         @RegistrationState
-        private int mInitialRegistrationState;
+        private int mNetworkRegistrationState;
 
         @NetworkType
         private int mAccessNetworkTechnology;
@@ -856,6 +902,31 @@
         public Builder() {}
 
         /**
+         * Builder from the existing {@link NetworkRegistrationInfo}.
+         *
+         * @param nri The network registration info object.
+         * @hide
+         */
+        public Builder(@NonNull NetworkRegistrationInfo nri) {
+            mDomain = nri.mDomain;
+            mTransportType = nri.mTransportType;
+            mNetworkRegistrationState = nri.mNetworkRegistrationState;
+            mAccessNetworkTechnology = nri.mAccessNetworkTechnology;
+            mRejectCause = nri.mRejectCause;
+            mEmergencyOnly = nri.mEmergencyOnly;
+            mAvailableServices = new ArrayList<>(nri.mAvailableServices);
+            mCellIdentity = nri.mCellIdentity;
+            if (nri.mDataSpecificInfo != null) {
+                mDataSpecificRegistrationInfo = new DataSpecificRegistrationInfo(
+                        nri.mDataSpecificInfo);
+            }
+            if (nri.mVoiceSpecificInfo != null) {
+                mVoiceSpecificRegistrationInfo = new VoiceSpecificRegistrationInfo(
+                        nri.mVoiceSpecificInfo);
+            }
+        }
+
+        /**
          * Set the network domain.
          *
          * @param domain Network domain.
@@ -887,7 +958,7 @@
          * @return The same instance of the builder.
          */
         public @NonNull Builder setRegistrationState(@RegistrationState int registrationState) {
-            mInitialRegistrationState = registrationState;
+            mNetworkRegistrationState = registrationState;
             return this;
         }
 
@@ -1006,7 +1077,7 @@
          */
         @SystemApi
         public @NonNull NetworkRegistrationInfo build() {
-            return new NetworkRegistrationInfo(mDomain, mTransportType, mInitialRegistrationState,
+            return new NetworkRegistrationInfo(mDomain, mTransportType, mNetworkRegistrationState,
                     mAccessNetworkTechnology, mRejectCause, mEmergencyOnly, mAvailableServices,
                     mCellIdentity, mRplmn, mVoiceSpecificRegistrationInfo,
                     mDataSpecificRegistrationInfo);
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index ac74016..03e019d 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -631,11 +631,17 @@
     }
 
     /**
-     * Get current roaming indicator of phone
+     * Get current roaming indicator of phone. This roaming state could be overridden by the carrier
+     * config.
      * (note: not just decoding from TS 27.007 7.2)
-     *
+     * @see TelephonyDisplayInfo#isRoaming() for visualization purpose.
      * @return true if TS 27.007 7.2 roaming is true
      *              and ONS is different from SPN
+     * @see CarrierConfigManager#KEY_FORCE_HOME_NETWORK_BOOL
+     * @see CarrierConfigManager#KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
+     * @see CarrierConfigManager#KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
+     * @see CarrierConfigManager#KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY
+     * @see CarrierConfigManager#KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY
      */
     public boolean getRoaming() {
         return getVoiceRoaming() || getDataRoaming();
@@ -650,8 +656,9 @@
     public boolean getVoiceRoaming() {
         return getVoiceRoamingType() != ROAMING_TYPE_NOT_ROAMING;
     }
+
     /**
-     * Get current voice network roaming type
+     * Get current voice roaming type. This roaming type could be overridden by the carrier config.
      * @return roaming type
      * @hide
      */
@@ -701,7 +708,7 @@
     }
 
     /**
-     * Get current data network roaming type
+     * Get current data roaming type. This roaming type could be overridden by the carrier config.
      * @return roaming type
      * @hide
      */
@@ -1207,8 +1214,13 @@
 
     /**
      * Initialize the service state. Set everything to the default value.
+     *
+     * @param legacyMode {@code true} if the device is on IWLAN legacy mode, where IWLAN is
+     * considered as a RAT on WWAN {@link NetworkRegistrationInfo}. {@code false} if the device
+     * is on AP-assisted mode, where IWLAN should be reported through WLAN.
+     * {@link NetworkRegistrationInfo}.
      */
-    private void init() {
+    private void init(boolean legacyMode) {
         if (DBG) Rlog.d(LOG_TAG, "init");
         mVoiceRegState = STATE_OUT_OF_SERVICE;
         mDataRegState = STATE_OUT_OF_SERVICE;
@@ -1240,11 +1252,13 @@
                     .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
                     .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN)
                     .build());
-            addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
-                    .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
-                    .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
-                    .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN)
-                    .build());
+            if (!legacyMode) {
+                addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+                        .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                        .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
+                        .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN)
+                        .build());
+            }
         }
         mOperatorAlphaLongRaw = null;
         mOperatorAlphaShortRaw = null;
@@ -1253,11 +1267,11 @@
     }
 
     public void setStateOutOfService() {
-        init();
+        init(true);
     }
 
     public void setStateOff() {
-        init();
+        init(true);
         mVoiceRegState = STATE_POWER_OFF;
         mDataRegState = STATE_POWER_OFF;
     }
@@ -1265,11 +1279,14 @@
     /**
      * Set the service state to out-of-service
      *
+     * @param legacyMode {@code true} if the device is on IWLAN legacy mode, where IWLAN is
+     * considered as a RAT on WWAN {@link NetworkRegistrationInfo}. {@code false} if the device
+     * is on AP-assisted mode, where IWLAN should be reported through WLAN.
      * @param powerOff {@code true} if this is a power off case (i.e. Airplane mode on).
      * @hide
      */
-    public void setOutOfService(boolean powerOff) {
-        init();
+    public void setOutOfService(boolean legacyMode, boolean powerOff) {
+        init(legacyMode);
         if (powerOff) {
             mVoiceRegState = STATE_POWER_OFF;
             mDataRegState = STATE_POWER_OFF;
diff --git a/telephony/java/android/telephony/TelephonyDisplayInfo.java b/telephony/java/android/telephony/TelephonyDisplayInfo.java
index f4e2ade..e01b10e 100644
--- a/telephony/java/android/telephony/TelephonyDisplayInfo.java
+++ b/telephony/java/android/telephony/TelephonyDisplayInfo.java
@@ -87,10 +87,12 @@
     public static final int OVERRIDE_NETWORK_TYPE_NR_ADVANCED = 5;
 
     @NetworkType
-    private final  int mNetworkType;
+    private final int mNetworkType;
 
     @OverrideNetworkType
-    private final  int mOverrideNetworkType;
+    private final int mOverrideNetworkType;
+
+    private final boolean mIsRoaming;
 
     /**
      * Constructor
@@ -98,18 +100,37 @@
      * @param networkType Current packet-switching cellular network type
      * @param overrideNetworkType The override network type
      *
+     * @deprecated will not use this constructor anymore.
+     * @hide
+     */
+    @Deprecated
+    public TelephonyDisplayInfo(@NetworkType int networkType,
+            @OverrideNetworkType int overrideNetworkType) {
+        this(networkType, overrideNetworkType, false);
+    }
+
+    /**
+     * Constructor
+     *
+     * @param networkType Current packet-switching cellular network type
+     * @param overrideNetworkType The override network type
+     * @param isRoaming True if the device is roaming after override.
+     *
      * @hide
      */
     public TelephonyDisplayInfo(@NetworkType int networkType,
-            @OverrideNetworkType int overrideNetworkType) {
+            @OverrideNetworkType int overrideNetworkType,
+            boolean isRoaming) {
         mNetworkType = networkType;
         mOverrideNetworkType = overrideNetworkType;
+        mIsRoaming = isRoaming;
     }
 
     /** @hide */
     public TelephonyDisplayInfo(Parcel p) {
         mNetworkType = p.readInt();
         mOverrideNetworkType = p.readInt();
+        mIsRoaming = p.readBoolean();
     }
 
     /**
@@ -135,10 +156,25 @@
         return mOverrideNetworkType;
     }
 
+    /**
+     * Get device is roaming or not. Note the isRoaming is for market branding or visualization
+     * purposes only. It cannot be treated as the actual roaming device is camped on.
+     *
+     * @return True if the device is registered on roaming network overridden by config.
+     * @see CarrierConfigManager#KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
+     * @see CarrierConfigManager#KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
+     * @see CarrierConfigManager#KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY
+     * @see CarrierConfigManager#KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY
+     */
+    public boolean isRoaming() {
+        return mIsRoaming;
+    }
+
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeInt(mNetworkType);
         dest.writeInt(mOverrideNetworkType);
+        dest.writeBoolean(mIsRoaming);
     }
 
     public static final @NonNull Parcelable.Creator<TelephonyDisplayInfo> CREATOR =
@@ -165,12 +201,13 @@
         if (o == null || getClass() != o.getClass()) return false;
         TelephonyDisplayInfo that = (TelephonyDisplayInfo) o;
         return mNetworkType == that.mNetworkType
-                && mOverrideNetworkType == that.mOverrideNetworkType;
+                && mOverrideNetworkType == that.mOverrideNetworkType
+                && mIsRoaming == that.mIsRoaming;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mNetworkType, mOverrideNetworkType);
+        return Objects.hash(mNetworkType, mOverrideNetworkType, mIsRoaming);
     }
 
     /**
@@ -195,6 +232,7 @@
     @Override
     public String toString() {
         return "TelephonyDisplayInfo {network=" + TelephonyManager.getNetworkTypeName(mNetworkType)
-                + ", override=" + overrideNetworkTypeToString(mOverrideNetworkType) + "}";
+                + ", overrideNetwork=" + overrideNetworkTypeToString(mOverrideNetworkType)
+                + ", isRoaming=" + mIsRoaming + "}";
     }
 }
diff --git a/tests/SoundTriggerTestApp/OWNERS b/tests/SoundTriggerTestApp/OWNERS
index 9db19a3..a0fcfc5 100644
--- a/tests/SoundTriggerTestApp/OWNERS
+++ b/tests/SoundTriggerTestApp/OWNERS
@@ -1,2 +1,2 @@
-include /core/java/android/media/soundtrigger/OWNERS
+include /media/java/android/media/soundtrigger/OWNERS
 mdooley@google.com
diff --git a/tests/SoundTriggerTests/OWNERS b/tests/SoundTriggerTests/OWNERS
index 816bc6b..1e41886 100644
--- a/tests/SoundTriggerTests/OWNERS
+++ b/tests/SoundTriggerTests/OWNERS
@@ -1 +1 @@
-include /core/java/android/media/soundtrigger/OWNERS
+include /media/java/android/media/soundtrigger/OWNERS
diff --git a/tests/utils/testutils/java/android/os/test/FakePermissionEnforcer.java b/tests/utils/testutils/java/android/os/test/FakePermissionEnforcer.java
new file mode 100644
index 0000000..b94bb41
--- /dev/null
+++ b/tests/utils/testutils/java/android/os/test/FakePermissionEnforcer.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.test;
+
+import static android.permission.PermissionManager.PERMISSION_GRANTED;
+import static android.permission.PermissionManager.PERMISSION_HARD_DENIED;
+
+import android.annotation.NonNull;
+import android.content.AttributionSource;
+import android.os.PermissionEnforcer;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Fake for {@link PermissionEnforcer}. Useful for tests wanting to mock the
+ * permission checks of an AIDL service. FakePermissionEnforcer may be passed
+ * to the constructor of the AIDL-generated Stub class.
+ *
+ */
+public class FakePermissionEnforcer extends PermissionEnforcer {
+    private Set<String> mGranted;
+
+    public FakePermissionEnforcer() {
+        mGranted = new HashSet();
+    }
+
+    public void grant(String permission) {
+        mGranted.add(permission);
+    }
+
+    public void revoke(String permission) {
+        mGranted.remove(permission);
+    }
+
+    private boolean granted(String permission) {
+        return mGranted.contains(permission);
+    }
+
+    @Override
+    protected int checkPermission(@NonNull String permission,
+              @NonNull AttributionSource source) {
+        return granted(permission) ? PERMISSION_GRANTED : PERMISSION_HARD_DENIED;
+    }
+
+    @Override
+    protected int checkPermission(@NonNull String permission, int pid, int uid) {
+        return granted(permission) ? PERMISSION_GRANTED : PERMISSION_HARD_DENIED;
+    }
+}
diff --git a/tests/utils/testutils/java/android/os/test/OWNERS b/tests/utils/testutils/java/android/os/test/OWNERS
new file mode 100644
index 0000000..3a9129e
--- /dev/null
+++ b/tests/utils/testutils/java/android/os/test/OWNERS
@@ -0,0 +1 @@
+per-file FakePermissionEnforcer.java = file:/tests/EnforcePermission/OWNERS
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 075bc5e..4123f80 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -17,7 +17,9 @@
 package com.android.server;
 
 import static android.net.ConnectivityManager.NetworkCallback;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -67,7 +69,6 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
-import android.net.TelephonyNetworkSpecifier;
 import android.net.Uri;
 import android.net.vcn.IVcnStatusCallback;
 import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
@@ -128,6 +129,15 @@
     private static final VcnConfig TEST_VCN_CONFIG;
     private static final VcnConfig TEST_VCN_CONFIG_PKG_2;
     private static final int TEST_UID = Process.FIRST_APPLICATION_UID;
+    private static final String TEST_IFACE_NAME = "TEST_IFACE";
+    private static final String TEST_IFACE_NAME_2 = "TEST_IFACE2";
+    private static final LinkProperties TEST_LP_1 = new LinkProperties();
+    private static final LinkProperties TEST_LP_2 = new LinkProperties();
+
+    static {
+        TEST_LP_1.setInterfaceName(TEST_IFACE_NAME);
+        TEST_LP_2.setInterfaceName(TEST_IFACE_NAME_2);
+    }
 
     static {
         final Context mockConfigContext = mock(Context.class);
@@ -1034,8 +1044,7 @@
         setupSubscriptionAndStartVcn(subId, subGrp, isVcnActive);
 
         return mVcnMgmtSvc.getUnderlyingNetworkPolicy(
-                getNetworkCapabilitiesBuilderForTransport(subId, transport).build(),
-                new LinkProperties());
+                getNetworkCapabilitiesBuilderForTransport(subId, transport).build(), TEST_LP_1);
     }
 
     private void checkGetRestrictedTransportsFromCarrierConfig(
@@ -1260,7 +1269,7 @@
                 false /* expectRestricted */);
     }
 
-    private void setupTrackedCarrierWifiNetwork(NetworkCapabilities caps) {
+    private void setupTrackedNetwork(NetworkCapabilities caps, LinkProperties lp) {
         mVcnMgmtSvc.systemReady();
 
         final ArgumentCaptor<NetworkCallback> captor =
@@ -1269,7 +1278,10 @@
                 .registerNetworkCallback(
                         eq(new NetworkRequest.Builder().clearCapabilities().build()),
                         captor.capture());
-        captor.getValue().onCapabilitiesChanged(mock(Network.class, CALLS_REAL_METHODS), caps);
+
+        Network mockNetwork = mock(Network.class, CALLS_REAL_METHODS);
+        captor.getValue().onCapabilitiesChanged(mockNetwork, caps);
+        captor.getValue().onLinkPropertiesChanged(mockNetwork, lp);
     }
 
     @Test
@@ -1279,7 +1291,7 @@
                 getNetworkCapabilitiesBuilderForTransport(TEST_SUBSCRIPTION_ID, TRANSPORT_WIFI)
                         .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
                         .build();
-        setupTrackedCarrierWifiNetwork(existingNetworkCaps);
+        setupTrackedNetwork(existingNetworkCaps, TEST_LP_1);
 
         // Trigger test without VCN instance alive; expect restart due to change of NOT_RESTRICTED
         // immutable capability
@@ -1288,7 +1300,7 @@
                         getNetworkCapabilitiesBuilderForTransport(
                                         TEST_SUBSCRIPTION_ID, TRANSPORT_WIFI)
                                 .build(),
-                        new LinkProperties());
+                        TEST_LP_1);
         assertTrue(policy.isTeardownRequested());
     }
 
@@ -1298,7 +1310,7 @@
         final NetworkCapabilities existingNetworkCaps =
                 getNetworkCapabilitiesBuilderForTransport(TEST_SUBSCRIPTION_ID, TRANSPORT_WIFI)
                         .build();
-        setupTrackedCarrierWifiNetwork(existingNetworkCaps);
+        setupTrackedNetwork(existingNetworkCaps, TEST_LP_1);
 
         final VcnUnderlyingNetworkPolicy policy =
                 startVcnAndGetPolicyForTransport(
@@ -1315,7 +1327,7 @@
                         .addCapability(NET_CAPABILITY_NOT_RESTRICTED)
                         .removeCapability(NET_CAPABILITY_IMS)
                         .build();
-        setupTrackedCarrierWifiNetwork(existingNetworkCaps);
+        setupTrackedNetwork(existingNetworkCaps, TEST_LP_1);
 
         final VcnUnderlyingNetworkPolicy policy =
                 mVcnMgmtSvc.getUnderlyingNetworkPolicy(
@@ -1336,7 +1348,7 @@
                 new NetworkCapabilities.Builder()
                         .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                         .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
-                        .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID_2))
+                        .setSubscriptionIds(Collections.singleton(TEST_SUBSCRIPTION_ID_2))
                         .build();
 
         VcnUnderlyingNetworkPolicy policy =
@@ -1346,6 +1358,38 @@
         assertEquals(nc, policy.getMergedNetworkCapabilities());
     }
 
+    /**
+     * Checks that networks with similar capabilities do not clobber each other.
+     *
+     * <p>In previous iterations, the VcnMgmtSvc used capability-matching to check if a network
+     * undergoing policy checks were the same as an existing networks. However, this meant that if
+     * there were newly added capabilities that the VCN did not check, two networks differing only
+     * by that capability would restart each other constantly.
+     */
+    @Test
+    public void testGetUnderlyingNetworkPolicySimilarNetworks() throws Exception {
+        NetworkCapabilities nc1 =
+                new NetworkCapabilities.Builder()
+                        .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+                        .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+                        .addCapability(NET_CAPABILITY_INTERNET)
+                        .setSubscriptionIds(Collections.singleton(TEST_SUBSCRIPTION_ID_2))
+                        .build();
+
+        NetworkCapabilities nc2 =
+                new NetworkCapabilities.Builder(nc1)
+                        .addCapability(NET_CAPABILITY_ENTERPRISE)
+                        .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
+                        .build();
+
+        setupTrackedNetwork(nc1, TEST_LP_1);
+
+        VcnUnderlyingNetworkPolicy policy = mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc2, TEST_LP_2);
+
+        assertFalse(policy.isTeardownRequested());
+        assertEquals(nc2, policy.getMergedNetworkCapabilities());
+    }
+
     @Test(expected = SecurityException.class)
     public void testGetUnderlyingNetworkPolicyInvalidPermission() {
         doReturn(PackageManager.PERMISSION_DENIED)
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
index 629e988..2266041 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -95,6 +95,7 @@
     private static final NetworkCapabilities WIFI_NETWORK_CAPABILITIES =
             new NetworkCapabilities.Builder()
                     .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                    .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                     .setSignalStrength(WIFI_RSSI)
                     .setSsid(SSID)
                     .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
@@ -509,12 +510,14 @@
             VcnCellUnderlyingNetworkTemplate template, boolean expectMatch) {
         assertEquals(
                 expectMatch,
-                checkMatchesCellPriorityRule(
+                checkMatchesPriorityRule(
                         mVcnContext,
                         template,
                         mCellNetworkRecord,
                         SUB_GROUP,
-                        mSubscriptionSnapshot));
+                        mSubscriptionSnapshot,
+                        null /* currentlySelected */,
+                        null /* carrierConfig */));
     }
 
     @Test