Merge "Hide SAW subwindows" into tm-dev
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index f7fe9ca..4e572fd 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -310,11 +310,13 @@
                     bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
                             | Context.BIND_ALMOST_PERCEPTIBLE
                             | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS
-                            | Context.BIND_NOT_APP_COMPONENT_USAGE;
+                            | Context.BIND_NOT_APP_COMPONENT_USAGE
+                            | Context.BIND_DENY_ACTIVITY_STARTS_PRE_34;
                 } else {
                     bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
                             | Context.BIND_NOT_PERCEPTIBLE
-                            | Context.BIND_NOT_APP_COMPONENT_USAGE;
+                            | Context.BIND_NOT_APP_COMPONENT_USAGE
+                            | Context.BIND_DENY_ACTIVITY_STARTS_PRE_34;
                 }
                 binding = mContext.bindServiceAsUser(intent, this, bindFlags,
                         UserHandle.of(job.getUserId()));
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e8ed8b8..96cc1f8 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9389,13 +9389,6 @@
         public static final String SPATIAL_AUDIO_ENABLED = "spatial_audio_enabled";
 
         /**
-         * Internal collection of audio device inventory items
-         * The device item stored are {@link com.android.server.audio.AdiDeviceState}
-         * @hide
-         */
-        public static final String AUDIO_DEVICE_INVENTORY = "audio_device_inventory";
-
-        /**
          * Indicates whether notification display on the lock screen is enabled.
          * <p>
          * Type: int (0 for false, 1 for true)
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 7e8622a..679a222 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -2379,7 +2379,8 @@
         boolean connect(String engine) {
             Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
             intent.setPackage(engine);
-            return mContext.bindService(intent, this, Context.BIND_AUTO_CREATE);
+            return mContext.bindService(intent, this,
+                    Context.BIND_AUTO_CREATE | Context.BIND_DENY_ACTIVITY_STARTS_PRE_34);
         }
 
         @Override
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 993e4e7..765901a 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -93,6 +93,9 @@
             throw ex;
         }
 
+        if (peer.getUid() != Process.SYSTEM_UID) {
+            throw new ZygoteSecurityException("Only system UID is allowed to connect to Zygote.");
+        }
         isEof = false;
     }
 
diff --git a/core/java/com/android/internal/util/BinaryXmlSerializer.java b/core/java/com/android/internal/util/BinaryXmlSerializer.java
index f0ca1edb..ae969a8 100644
--- a/core/java/com/android/internal/util/BinaryXmlSerializer.java
+++ b/core/java/com/android/internal/util/BinaryXmlSerializer.java
@@ -97,6 +97,8 @@
      */
     private static final int BUFFER_SIZE = 32_768;
 
+    private static final int MAX_UNSIGNED_SHORT = 65_535;
+
     private FastDataOutput mOut;
 
     /**
@@ -226,6 +228,10 @@
         if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
         mOut.writeByte(ATTRIBUTE | TYPE_BYTES_HEX);
         mOut.writeInternedUTF(name);
+        if (value.length > MAX_UNSIGNED_SHORT) {
+            throw new IOException("attributeBytesHex: input size (" + value.length
+                    + ") exceeds maximum allowed size (" + MAX_UNSIGNED_SHORT + ")");
+        }
         mOut.writeShort(value.length);
         mOut.write(value);
         return this;
@@ -237,6 +243,10 @@
         if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
         mOut.writeByte(ATTRIBUTE | TYPE_BYTES_BASE64);
         mOut.writeInternedUTF(name);
+        if (value.length > MAX_UNSIGNED_SHORT) {
+            throw new IOException("attributeBytesBase64: input size (" + value.length
+                    + ") exceeds maximum allowed size (" + MAX_UNSIGNED_SHORT + ")");
+        }
         mOut.writeShort(value.length);
         mOut.write(value);
         return this;
diff --git a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
index add645de..b48fe41 100644
--- a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
+++ b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
@@ -353,6 +353,18 @@
   return result;
 }
 
+static uid_t getSocketPeerUid(int socket, const std::function<void(const std::string&)>& fail_fn) {
+  struct ucred credentials;
+  socklen_t cred_size = sizeof credentials;
+  if (getsockopt(socket, SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1
+      || cred_size != sizeof credentials) {
+    fail_fn(CREATE_ERROR("Failed to get socket credentials, %s",
+                         strerror(errno)));
+  }
+
+  return credentials.uid;
+}
+
 // Read all lines from the current command into the buffer, and then reset the buffer, so
 // we will start reading again at the beginning of the command, starting with the argument
 // count. And we don't need access to the fd to do so.
@@ -411,19 +423,12 @@
     fail_fn_z("Failed to retrieve session socket timeout");
   }
 
-  struct ucred credentials;
-  socklen_t cred_size = sizeof credentials;
-  if (getsockopt(n_buffer->getFd(), SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1
-      || cred_size != sizeof credentials) {
-    fail_fn_1(CREATE_ERROR("ForkRepeatedly failed to get initial credentials, %s",
-                           strerror(errno)));
+  uid_t peerUid = getSocketPeerUid(session_socket, fail_fn_1);
+  if (peerUid != static_cast<uid_t>(expected_uid)) {
+    return JNI_FALSE;
   }
-
   bool first_time = true;
   do {
-    if (credentials.uid != expected_uid) {
-      return JNI_FALSE;
-    }
     n_buffer->readAllLines(first_time ? fail_fn_1 : fail_fn_n);
     n_buffer->reset();
     int pid = zygote::forkApp(env, /* no pipe FDs */ -1, -1, session_socket_fds,
@@ -453,30 +458,56 @@
     // Clear buffer and get count from next command.
     n_buffer->clear();
     for (;;) {
+      bool valid_session_socket = true;
       // Poll isn't strictly necessary for now. But without it, disconnect is hard to detect.
       int poll_res = TEMP_FAILURE_RETRY(poll(fd_structs, 2, -1 /* infinite timeout */));
       if ((fd_structs[SESSION_IDX].revents & POLLIN) != 0) {
         if (n_buffer->getCount(fail_fn_z) != 0) {
           break;
-        }  // else disconnected;
+        } else {
+          // Session socket was disconnected
+          valid_session_socket = false;
+          close(session_socket);
+        }
       } else if (poll_res == 0 || (fd_structs[ZYGOTE_IDX].revents & POLLIN) == 0) {
         fail_fn_z(
             CREATE_ERROR("Poll returned with no descriptors ready! Poll returned %d", poll_res));
       }
-      // We've now seen either a disconnect or connect request.
-      close(session_socket);
-      int new_fd = TEMP_FAILURE_RETRY(accept(zygote_socket_fd, nullptr, nullptr));
+      int new_fd = -1;
+      do {
+        // We've now seen either a disconnect or connect request.
+        new_fd = TEMP_FAILURE_RETRY(accept(zygote_socket_fd, nullptr, nullptr));
+        if (new_fd == -1) {
+          fail_fn_z(CREATE_ERROR("Accept(%d) failed: %s", zygote_socket_fd, strerror(errno)));
+        }
+        uid_t newPeerUid = getSocketPeerUid(new_fd, fail_fn_1);
+        if (newPeerUid != static_cast<uid_t>(expected_uid)) {
+          ALOGW("Dropping new connection with a mismatched uid %d\n", newPeerUid);
+          close(new_fd);
+          new_fd = -1;
+        } else {
+          // If we still have a valid session socket, close it now
+          if (valid_session_socket) {
+              close(session_socket);
+          }
+          valid_session_socket = true;
+        }
+      } while (!valid_session_socket);
+
+      // At this point we either have a valid new connection (new_fd > 0), or
+      // an existing session socket we can poll on
       if (new_fd == -1) {
-        fail_fn_z(CREATE_ERROR("Accept(%d) failed: %s", zygote_socket_fd, strerror(errno)));
+        // The new connection wasn't valid, and we still have an old one; retry polling
+        continue;
       }
       if (new_fd != session_socket) {
-          // Move new_fd back to the old value, so that we don't have to change Java-level data
-          // structures to reflect a change. This implicitly closes the old one.
-          if (TEMP_FAILURE_RETRY(dup2(new_fd, session_socket)) != session_socket) {
-            fail_fn_z(CREATE_ERROR("Failed to move fd %d to %d: %s",
-                                   new_fd, session_socket, strerror(errno)));
-          }
-          close(new_fd);  //  On Linux, fd is closed even if EINTR is returned.
+        // Move new_fd back to the old value, so that we don't have to change Java-level data
+        // structures to reflect a change. This implicitly closes the old one.
+        if (TEMP_FAILURE_RETRY(dup2(new_fd, session_socket)) != session_socket) {
+          fail_fn_z(CREATE_ERROR("Failed to move fd %d to %d: %s",
+                                 new_fd, session_socket, strerror(errno)));
+        }
+        close(new_fd);  //  On Linux, fd is closed even if EINTR is returned.
       }
       // If we ever return, we effectively reuse the old Java ZygoteConnection.
       // None of its state needs to change.
@@ -488,13 +519,6 @@
         fail_fn_z(CREATE_ERROR("Failed to set send timeout for socket %d: %s",
                                session_socket, strerror(errno)));
       }
-      if (getsockopt(session_socket, SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1) {
-        fail_fn_z(CREATE_ERROR("ForkMany failed to get credentials: %s", strerror(errno)));
-      }
-      if (cred_size != sizeof credentials) {
-        fail_fn_z(CREATE_ERROR("ForkMany credential size = %d, should be %d",
-                               cred_size, static_cast<int>(sizeof credentials)));
-      }
     }
     first_time = false;
   } while (n_buffer->isSimpleForkCommand(minUid, fail_fn_n));
diff --git a/core/tests/coretests/src/android/util/BinaryXmlTest.java b/core/tests/coretests/src/android/util/BinaryXmlTest.java
index fd625dce..b369868 100644
--- a/core/tests/coretests/src/android/util/BinaryXmlTest.java
+++ b/core/tests/coretests/src/android/util/BinaryXmlTest.java
@@ -24,6 +24,8 @@
 import static android.util.XmlTest.doVerifyWrite;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.fail;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
 import android.os.PersistableBundle;
@@ -38,12 +40,15 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
 
 @RunWith(AndroidJUnit4.class)
 public class BinaryXmlTest {
+    private static final int MAX_UNSIGNED_SHORT = 65_535;
+
     /**
      * Verify that we can write and read large numbers of interned
      * {@link String} values.
@@ -167,4 +172,49 @@
             }
         }
     }
+
+    @Test
+    public void testAttributeBytes_BinaryDataOverflow() throws Exception {
+        final TypedXmlSerializer out = Xml.newBinarySerializer();
+        final ByteArrayOutputStream os = new ByteArrayOutputStream();
+        out.setOutput(os, StandardCharsets.UTF_8.name());
+
+        final byte[] testBytes = new byte[MAX_UNSIGNED_SHORT + 1];
+        assertThrows(IOException.class,
+                () -> out.attributeBytesHex(/* namespace */ null, /* name */ "attributeBytesHex",
+                        testBytes));
+
+        assertThrows(IOException.class,
+                () -> out.attributeBytesBase64(/* namespace */ null, /* name */
+                        "attributeBytesBase64", testBytes));
+    }
+
+    @Test
+    public void testAttributeBytesHex_MaximumBinaryData() throws Exception {
+        final TypedXmlSerializer out = Xml.newBinarySerializer();
+        final ByteArrayOutputStream os = new ByteArrayOutputStream();
+        out.setOutput(os, StandardCharsets.UTF_8.name());
+
+        final byte[] testBytes = new byte[MAX_UNSIGNED_SHORT];
+        try {
+            out.attributeBytesHex(/* namespace */ null, /* name */ "attributeBytesHex", testBytes);
+        } catch (Exception e) {
+            fail("testAttributeBytesHex fails with exception: " + e.toString());
+        }
+    }
+
+    @Test
+    public void testAttributeBytesBase64_MaximumBinaryData() throws Exception {
+        final TypedXmlSerializer out = Xml.newBinarySerializer();
+        final ByteArrayOutputStream os = new ByteArrayOutputStream();
+        out.setOutput(os, StandardCharsets.UTF_8.name());
+
+        final byte[] testBytes = new byte[MAX_UNSIGNED_SHORT];
+        try {
+            out.attributeBytesBase64(/* namespace */ null, /* name */ "attributeBytesBase64",
+                    testBytes);
+        } catch (Exception e) {
+            fail("testAttributeBytesBase64 fails with exception: " + e.toString());
+        }
+    }
 }
diff --git a/media/java/android/media/AudioDeviceAttributes.java b/media/java/android/media/AudioDeviceAttributes.java
index 5a27435..af3c295 100644
--- a/media/java/android/media/AudioDeviceAttributes.java
+++ b/media/java/android/media/AudioDeviceAttributes.java
@@ -68,7 +68,7 @@
     /**
      * The unique address of the device. Some devices don't have addresses, only an empty string.
      */
-    private @NonNull String mAddress;
+    private final @NonNull String mAddress;
     /**
      * The non-unique name of the device. Some devices don't have names, only an empty string.
      * Should not be used as a unique identifier for a device.
@@ -188,21 +188,6 @@
 
     /**
      * @hide
-     * Copy Constructor.
-     * @param ada the copied AudioDeviceAttributes
-     */
-    public AudioDeviceAttributes(AudioDeviceAttributes ada) {
-        mRole = ada.getRole();
-        mType = ada.getType();
-        mAddress = ada.getAddress();
-        mName = ada.getName();
-        mNativeType = ada.getInternalType();
-        mAudioProfiles = ada.getAudioProfiles();
-        mAudioDescriptors = ada.getAudioDescriptors();
-    }
-
-    /**
-     * @hide
      * Returns the role of a device
      * @return the role
      */
@@ -233,15 +218,6 @@
 
     /**
      * @hide
-     * Sets the device address. Only used by audio service.
-     */
-    public void setAddress(@NonNull String address) {
-        Objects.requireNonNull(address);
-        mAddress = address;
-    }
-
-    /**
-     * @hide
      * Returns the name of the audio device, or an empty string for devices without one
      * @return the device name
      */
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 4674640..955bfcc 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1208,9 +1208,6 @@
     public static final Set<Integer> DEVICE_IN_ALL_SCO_SET;
     /** @hide */
     public static final Set<Integer> DEVICE_IN_ALL_USB_SET;
-    /** @hide */
-    public static final Set<Integer> DEVICE_IN_ALL_BLE_SET;
-
     static {
         DEVICE_IN_ALL_SET = new HashSet<>();
         DEVICE_IN_ALL_SET.add(DEVICE_IN_COMMUNICATION);
@@ -1250,66 +1247,6 @@
         DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_ACCESSORY);
         DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_DEVICE);
         DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_HEADSET);
-
-        DEVICE_IN_ALL_BLE_SET = new HashSet<>();
-        DEVICE_IN_ALL_BLE_SET.add(DEVICE_IN_BLE_HEADSET);
-    }
-
-    /** @hide */
-    public static boolean isBluetoothDevice(int deviceType) {
-        return isBluetoothA2dpOutDevice(deviceType)
-                || isBluetoothScoDevice(deviceType)
-                || isBluetoothLeDevice(deviceType);
-    }
-
-    /** @hide */
-    public static boolean isBluetoothOutDevice(int deviceType) {
-        return isBluetoothA2dpOutDevice(deviceType)
-                || isBluetoothScoOutDevice(deviceType)
-                || isBluetoothLeOutDevice(deviceType);
-    }
-
-    /** @hide */
-    public static boolean isBluetoothInDevice(int deviceType) {
-        return isBluetoothScoInDevice(deviceType)
-                || isBluetoothLeInDevice(deviceType);
-    }
-
-    /** @hide */
-    public static boolean isBluetoothA2dpOutDevice(int deviceType) {
-        return DEVICE_OUT_ALL_A2DP_SET.contains(deviceType);
-    }
-
-    /** @hide */
-    public static boolean isBluetoothScoOutDevice(int deviceType) {
-        return DEVICE_OUT_ALL_SCO_SET.contains(deviceType);
-    }
-
-    /** @hide */
-    public static boolean isBluetoothScoInDevice(int deviceType) {
-        return DEVICE_IN_ALL_SCO_SET.contains(deviceType);
-    }
-
-    /** @hide */
-    public static boolean isBluetoothScoDevice(int deviceType) {
-        return isBluetoothScoOutDevice(deviceType)
-                || isBluetoothScoInDevice(deviceType);
-    }
-
-    /** @hide */
-    public static boolean isBluetoothLeOutDevice(int deviceType) {
-        return DEVICE_OUT_ALL_BLE_SET.contains(deviceType);
-    }
-
-    /** @hide */
-    public static boolean isBluetoothLeInDevice(int deviceType) {
-        return DEVICE_IN_ALL_BLE_SET.contains(deviceType);
-    }
-
-    /** @hide */
-    public static boolean isBluetoothLeDevice(int deviceType) {
-        return isBluetoothLeOutDevice(deviceType)
-                || isBluetoothLeInDevice(deviceType);
     }
 
     /** @hide */
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index da0689c..cce5154 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -687,7 +687,6 @@
                  Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
                  Settings.Secure.ASSIST_STRUCTURE_ENABLED,
                  Settings.Secure.ATTENTIVE_TIMEOUT,
-                 Settings.Secure.AUDIO_DEVICE_INVENTORY, // setting not controllable by user
                  Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION,
                  Settings.Secure.AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT,
                  Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java
index ba6b1dd..3681c2c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java
@@ -30,6 +30,7 @@
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.qs.dagger.QSScope;
 
 import java.util.concurrent.Executor;
@@ -51,6 +52,7 @@
     private final Context mContext;
     private final Executor mMainExecutor;
     private final Executor mExecutor;
+    private final ActivityStarter mActivityStarter;
 
     private final FgsManagerController mFgsManagerController;
 
@@ -69,7 +71,8 @@
     @Inject
     QSFgsManagerFooter(@Named(QS_FGS_MANAGER_FOOTER_VIEW) View rootView,
             @Main Executor mainExecutor, @Background Executor executor,
-            FgsManagerController fgsManagerController) {
+            FgsManagerController fgsManagerController,
+            ActivityStarter activityStarter) {
         mRootView = rootView;
         mFooterText = mRootView.findViewById(R.id.footer_text);
         mTextContainer = mRootView.findViewById(R.id.fgs_text_container);
@@ -81,6 +84,7 @@
         mMainExecutor = mainExecutor;
         mExecutor = executor;
         mFgsManagerController = fgsManagerController;
+        mActivityStarter = activityStarter;
     }
 
     /**
@@ -128,7 +132,14 @@
 
     @Override
     public void onClick(View view) {
-        mFgsManagerController.showDialog(mRootView);
+        mActivityStarter.dismissKeyguardThenExecute(
+            () -> {
+                mFgsManagerController.showDialog(mRootView);
+                return false /* if the dismiss should be deferred */;
+            },
+            null /* cancelAction */,
+            true /* afterKeyguardGone */
+        );
     }
 
     public void refreshState() {
diff --git a/services/core/java/com/android/server/audio/AdiDeviceState.java b/services/core/java/com/android/server/audio/AdiDeviceState.java
deleted file mode 100644
index eab1eca..0000000
--- a/services/core/java/com/android/server/audio/AdiDeviceState.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * 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 com.android.server.audio;
-
-import static android.media.AudioSystem.DEVICE_NONE;
-import static android.media.AudioSystem.isBluetoothDevice;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.media.AudioDeviceAttributes;
-import android.media.AudioDeviceInfo;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-
-import java.util.Objects;
-
-/**
- * Class representing all devices that were previously or are currently connected. Data is
- * persisted in {@link android.provider.Settings.Secure}
- */
-/*package*/ final class AdiDeviceState {
-    private static final String TAG = "AS.AdiDeviceState";
-
-    private static final String SETTING_FIELD_SEPARATOR = ",";
-
-    @AudioDeviceInfo.AudioDeviceType
-    private final int mDeviceType;
-
-    private final int mInternalDeviceType;
-    @NonNull
-    private final String mDeviceAddress;
-    /** Unique device id from internal device type and address. */
-    private final Pair<Integer, String> mDeviceId;
-    private boolean mSAEnabled;
-    private boolean mHasHeadTracker = false;
-    private boolean mHeadTrackerEnabled;
-
-    /**
-     * Constructor
-     *
-     * @param deviceType external audio device type
-     * @param internalDeviceType if not set pass {@link DEVICE_NONE}, in this case the
-     *                           default conversion of the external type will be used
-     * @param address must be non-null for wireless devices
-     * @throws NullPointerException if a null address is passed for a wireless device
-     */
-    AdiDeviceState(@AudioDeviceInfo.AudioDeviceType int deviceType,
-                        int internalDeviceType,
-                        @Nullable String address) {
-        mDeviceType = deviceType;
-        if (internalDeviceType != DEVICE_NONE) {
-            mInternalDeviceType = internalDeviceType;
-        } else {
-            mInternalDeviceType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(deviceType);
-
-        }
-        mDeviceAddress = isBluetoothDevice(mInternalDeviceType) ? Objects.requireNonNull(
-                address) : "";
-        mDeviceId = new Pair<>(mInternalDeviceType, mDeviceAddress);
-    }
-
-    public Pair<Integer, String> getDeviceId() {
-        return mDeviceId;
-    }
-
-
-
-    @AudioDeviceInfo.AudioDeviceType
-    public int getDeviceType() {
-        return mDeviceType;
-    }
-
-    public int getInternalDeviceType() {
-        return mInternalDeviceType;
-    }
-
-    @NonNull
-    public String getDeviceAddress() {
-        return mDeviceAddress;
-    }
-
-    public void setSAEnabled(boolean sAEnabled) {
-        mSAEnabled = sAEnabled;
-    }
-
-    public boolean isSAEnabled() {
-        return mSAEnabled;
-    }
-
-    public void setHeadTrackerEnabled(boolean headTrackerEnabled) {
-        mHeadTrackerEnabled = headTrackerEnabled;
-    }
-
-    public boolean isHeadTrackerEnabled() {
-        return mHeadTrackerEnabled;
-    }
-
-    public void setHasHeadTracker(boolean hasHeadTracker) {
-        mHasHeadTracker = hasHeadTracker;
-    }
-
-
-    public boolean hasHeadTracker() {
-        return mHasHeadTracker;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        // type check and cast
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        final AdiDeviceState sads = (AdiDeviceState) obj;
-        return mDeviceType == sads.mDeviceType
-                && mInternalDeviceType == sads.mInternalDeviceType
-                && mDeviceAddress.equals(sads.mDeviceAddress)  // NonNull
-                && mSAEnabled == sads.mSAEnabled
-                && mHasHeadTracker == sads.mHasHeadTracker
-                && mHeadTrackerEnabled == sads.mHeadTrackerEnabled;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mDeviceType, mInternalDeviceType, mDeviceAddress, mSAEnabled,
-                mHasHeadTracker, mHeadTrackerEnabled);
-    }
-
-    @Override
-    public String toString() {
-        return "type: " + mDeviceType
-                + " internal type: 0x" + Integer.toHexString(mInternalDeviceType)
-                + " addr: " + mDeviceAddress + " enabled: " + mSAEnabled
-                + " HT: " + mHasHeadTracker + " HTenabled: " + mHeadTrackerEnabled;
-    }
-
-    public String toPersistableString() {
-        return (new StringBuilder().append(mDeviceType)
-                .append(SETTING_FIELD_SEPARATOR).append(mDeviceAddress)
-                .append(SETTING_FIELD_SEPARATOR).append(mSAEnabled ? "1" : "0")
-                .append(SETTING_FIELD_SEPARATOR).append(mHasHeadTracker ? "1" : "0")
-                .append(SETTING_FIELD_SEPARATOR).append(mHeadTrackerEnabled ? "1" : "0")
-                .append(SETTING_FIELD_SEPARATOR).append(mInternalDeviceType)
-                .toString());
-    }
-
-    /**
-     * Gets the max size (including separators) when persisting the elements with
-     * {@link AdiDeviceState#toPersistableString()}.
-     */
-    public static int getPeristedMaxSize() {
-        return 36;  /* (mDeviceType)2 + (mDeviceAddresss)17 + (mInternalDeviceType)9 + (mSAEnabled)1
-                           + (mHasHeadTracker)1 + (mHasHeadTrackerEnabled)1
-                           + (SETTINGS_FIELD_SEPARATOR)5 */
-    }
-
-    @Nullable
-    public static AdiDeviceState fromPersistedString(@Nullable String persistedString) {
-        if (persistedString == null) {
-            return null;
-        }
-        if (persistedString.isEmpty()) {
-            return null;
-        }
-        String[] fields = TextUtils.split(persistedString, SETTING_FIELD_SEPARATOR);
-        // we may have 5 fields for the legacy AdiDeviceState and 6 containing the internal
-        // device type
-        if (fields.length != 5 && fields.length != 6) {
-            // expecting all fields, fewer may mean corruption, ignore those settings
-            return null;
-        }
-        try {
-            final int deviceType = Integer.parseInt(fields[0]);
-            int internalDeviceType = -1;
-            if (fields.length == 6) {
-                internalDeviceType = Integer.parseInt(fields[5]);
-            }
-            final AdiDeviceState deviceState = new AdiDeviceState(deviceType,
-                    internalDeviceType, fields[1]);
-            deviceState.setHasHeadTracker(Integer.parseInt(fields[2]) == 1);
-            deviceState.setHasHeadTracker(Integer.parseInt(fields[3]) == 1);
-            deviceState.setHeadTrackerEnabled(Integer.parseInt(fields[4]) == 1);
-            return deviceState;
-        } catch (NumberFormatException e) {
-            Log.e(TAG, "unable to parse setting for AdiDeviceState: " + persistedString, e);
-            return null;
-        }
-    }
-
-    public AudioDeviceAttributes getAudioDeviceAttributes() {
-        return new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
-                mDeviceType, mDeviceAddress);
-    }
-
-}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 6ccdd82..03dcc8d 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -46,7 +46,6 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.PrintWriterPrinter;
@@ -64,11 +63,8 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 
 
-/**
- * @hide
- * (non final for mocking/spying)
- */
-public class AudioDeviceBroker {
+/** @hide */
+/*package*/ final class AudioDeviceBroker {
 
     private static final String TAG = "AS.AudioDeviceBroker";
 
@@ -833,8 +829,8 @@
     }
 
     /*package*/ void registerStrategyPreferredDevicesDispatcher(
-            @NonNull IStrategyPreferredDevicesDispatcher dispatcher, boolean isPrivileged) {
-        mDeviceInventory.registerStrategyPreferredDevicesDispatcher(dispatcher, isPrivileged);
+            @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
+        mDeviceInventory.registerStrategyPreferredDevicesDispatcher(dispatcher);
     }
 
     /*package*/ void unregisterStrategyPreferredDevicesDispatcher(
@@ -852,8 +848,8 @@
     }
 
     /*package*/ void registerCapturePresetDevicesRoleDispatcher(
-            @NonNull ICapturePresetDevicesRoleDispatcher dispatcher, boolean isPrivileged) {
-        mDeviceInventory.registerCapturePresetDevicesRoleDispatcher(dispatcher, isPrivileged);
+            @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
+        mDeviceInventory.registerCapturePresetDevicesRoleDispatcher(dispatcher);
     }
 
     /*package*/ void unregisterCapturePresetDevicesRoleDispatcher(
@@ -861,11 +857,6 @@
         mDeviceInventory.unregisterCapturePresetDevicesRoleDispatcher(dispatcher);
     }
 
-    /* package */ List<AudioDeviceAttributes> anonymizeAudioDeviceAttributesListUnchecked(
-            List<AudioDeviceAttributes> devices) {
-        return mAudioService.anonymizeAudioDeviceAttributesListUnchecked(devices);
-    }
-
     /*package*/ void registerCommunicationDeviceDispatcher(
             @NonNull ICommunicationDeviceDispatcher dispatcher) {
         mCommDevDispatchers.register(dispatcher);
@@ -1471,9 +1462,6 @@
                     final int capturePreset = msg.arg1;
                     mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset);
                 } break;
-                case MSG_PERSIST_AUDIO_DEVICE_SETTINGS:
-                    onPersistAudioDeviceSettings();
-                    break;
                 default:
                     Log.wtf(TAG, "Invalid message " + msg.what);
             }
@@ -1546,8 +1534,6 @@
     // process set volume for Le Audio, obj is BleVolumeInfo
     private static final int MSG_II_SET_LE_AUDIO_OUT_VOLUME = 46;
 
-    private static final int MSG_PERSIST_AUDIO_DEVICE_SETTINGS = 54;
-
     private static boolean isMessageHandledUnderWakelock(int msgId) {
         switch(msgId) {
             case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
@@ -1933,95 +1919,4 @@
             return mDeviceInventory.getDeviceSensorUuid(device);
         }
     }
-
-    /**
-     * post a message to persist the audio device settings.
-     * Message is delayed by 1s on purpose in case of successive changes in quick succession (at
-     * init time for instance)
-     * Note this method is made public to work around a Mockito bug where it needs to be public
-     * in order to be mocked by a test a the same package
-     * (see https://code.google.com/archive/p/mockito/issues/127)
-     */
-    public void persistAudioDeviceSettings() {
-        sendMsg(MSG_PERSIST_AUDIO_DEVICE_SETTINGS, SENDMSG_REPLACE, /*delay*/ 1000);
-    }
-
-    void onPersistAudioDeviceSettings() {
-        final String deviceSettings = mDeviceInventory.getDeviceSettings();
-        Log.v(TAG, "saving audio device settings: " + deviceSettings);
-        final SettingsAdapter settings = mAudioService.getSettings();
-        boolean res = settings.putSecureStringForUser(mAudioService.getContentResolver(),
-                Settings.Secure.AUDIO_DEVICE_INVENTORY,
-                deviceSettings, UserHandle.USER_CURRENT);
-        if (!res) {
-            Log.e(TAG, "error saving audio device settings: " + deviceSettings);
-        }
-    }
-
-    void onReadAudioDeviceSettings() {
-        final SettingsAdapter settingsAdapter = mAudioService.getSettings();
-        final ContentResolver contentResolver = mAudioService.getContentResolver();
-        String settings = settingsAdapter.getSecureStringForUser(contentResolver,
-                Settings.Secure.AUDIO_DEVICE_INVENTORY, UserHandle.USER_CURRENT);
-        if (settings == null) {
-            Log.i(TAG, "reading spatial audio device settings from legacy key"
-                    + Settings.Secure.SPATIAL_AUDIO_ENABLED);
-            // legacy string format for key SPATIAL_AUDIO_ENABLED has the same order of fields like
-            // the strings for key AUDIO_DEVICE_INVENTORY. This will ensure to construct valid
-            // device settings when calling {@link #setDeviceSettings()}
-            settings = settingsAdapter.getSecureStringForUser(contentResolver,
-                    Settings.Secure.SPATIAL_AUDIO_ENABLED, UserHandle.USER_CURRENT);
-            if (settings == null) {
-                Log.i(TAG, "no spatial audio device settings stored with legacy key");
-            } else if (!settings.equals("")) {
-                // Delete old key value and update the new key
-                if (!settingsAdapter.putSecureStringForUser(contentResolver,
-                        Settings.Secure.SPATIAL_AUDIO_ENABLED,
-                        /*value=*/"",
-                        UserHandle.USER_CURRENT)) {
-                    Log.w(TAG, "cannot erase the legacy audio device settings with key "
-                            + Settings.Secure.SPATIAL_AUDIO_ENABLED);
-                }
-                if (!settingsAdapter.putSecureStringForUser(contentResolver,
-                        Settings.Secure.AUDIO_DEVICE_INVENTORY,
-                        settings,
-                        UserHandle.USER_CURRENT)) {
-                    Log.e(TAG, "error updating the new audio device settings with key "
-                            + Settings.Secure.AUDIO_DEVICE_INVENTORY);
-                }
-            }
-        }
-
-        if (settings != null && !settings.equals("")) {
-            setDeviceSettings(settings);
-        }
-    }
-
-    void setDeviceSettings(String settings) {
-        mDeviceInventory.setDeviceSettings(settings);
-    }
-
-    /** Test only method. */
-    String getDeviceSettings() {
-        return mDeviceInventory.getDeviceSettings();
-    }
-
-    List<AdiDeviceState> getImmutableDeviceInventory() {
-        return mDeviceInventory.getImmutableDeviceInventory();
-    }
-
-    void addDeviceStateToInventory(AdiDeviceState deviceState) {
-        mDeviceInventory.addDeviceStateToInventory(deviceState);
-    }
-
-    AdiDeviceState findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada,
-            int canonicalType) {
-        return mDeviceInventory.findDeviceStateForAudioDeviceAttributes(ada, canonicalType);
-    }
-
-    //------------------------------------------------
-    // for testing purposes only
-    void clearDeviceInventory() {
-        mDeviceInventory.clearDeviceInventory();
-    }
 }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 9524c54..dbe4fb8 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -15,8 +15,6 @@
  */
 package com.android.server.audio;
 
-import static android.media.AudioSystem.isBluetoothDevice;
-
 import android.annotation.NonNull;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
@@ -40,7 +38,6 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
-import android.util.Pair;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
@@ -48,9 +45,7 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Objects;
@@ -66,81 +61,12 @@
 
     private static final String TAG = "AS.AudioDeviceInventory";
 
-    private static final String SETTING_DEVICE_SEPARATOR_CHAR = "|";
-    private static final String SETTING_DEVICE_SEPARATOR = "\\|";
-
     // lock to synchronize all access to mConnectedDevices and mApmConnectedDevices
     private final Object mDevicesLock = new Object();
 
     //Audio Analytics ids.
     private static final String mMetricsId = "audio.device.";
 
-    private final Object mDeviceInventoryLock = new Object();
-    @GuardedBy("mDeviceInventoryLock")
-    private final HashMap<Pair<Integer, String>, AdiDeviceState> mDeviceInventory = new HashMap<>();
-
-    List<AdiDeviceState> getImmutableDeviceInventory() {
-        synchronized (mDeviceInventoryLock) {
-            return new ArrayList<AdiDeviceState>(mDeviceInventory.values());
-        }
-    }
-
-    void addDeviceStateToInventory(AdiDeviceState deviceState) {
-        synchronized (mDeviceInventoryLock) {
-            mDeviceInventory.put(deviceState.getDeviceId(), deviceState);
-        }
-    }
-
-    /**
-     * Adds a new entry in mDeviceInventory if the AudioDeviceAttributes passed is an sink
-     * Bluetooth device and no corresponding entry already exists.
-     * @param ada the device to add if needed
-     */
-    void addAudioDeviceInInventoryIfNeeded(AudioDeviceAttributes ada) {
-        if (!AudioSystem.isBluetoothOutDevice(ada.getInternalType())) {
-            return;
-        }
-        synchronized (mDeviceInventoryLock) {
-            if (findDeviceStateForAudioDeviceAttributes(ada, ada.getType()) != null) {
-                return;
-            }
-            AdiDeviceState ads = new AdiDeviceState(
-                    ada.getType(), ada.getInternalType(), ada.getAddress());
-            mDeviceInventory.put(ads.getDeviceId(), ads);
-        }
-        mDeviceBroker.persistAudioDeviceSettings();
-    }
-
-    /**
-     * Finds the device state that matches the passed {@link AudioDeviceAttributes} and device
-     * type. Note: currently this method only returns a valid device for A2DP and BLE devices.
-     *
-     * @param ada attributes of device to match
-     * @param canonicalDeviceType external device type to match
-     * @return the found {@link AdiDeviceState} matching a cached A2DP or BLE device or
-     *         {@code null} otherwise.
-     */
-    AdiDeviceState findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada,
-            int canonicalDeviceType) {
-        final boolean isWireless = isBluetoothDevice(ada.getInternalType());
-        synchronized (mDeviceInventoryLock) {
-            for (AdiDeviceState deviceState : mDeviceInventory.values()) {
-                if (deviceState.getDeviceType() == canonicalDeviceType
-                        && (!isWireless || ada.getAddress().equals(
-                        deviceState.getDeviceAddress()))) {
-                    return deviceState;
-                }
-            }
-        }
-        return null;
-    }
-
-    void clearDeviceInventory() {
-        synchronized (mDeviceInventoryLock) {
-            mDeviceInventory.clear();
-        }
-    }
-
     // List of connected devices
     // Key for map created from DeviceInfo.makeDeviceListKey()
     @GuardedBy("mDevicesLock")
@@ -336,12 +262,6 @@
         mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> {
             pw.println("  " + prefix + "capturePreset:" + capturePreset
                     + " devices:" + devices); });
-        pw.println("\ndevices:\n");
-        synchronized (mDeviceInventoryLock) {
-            for (AdiDeviceState device : mDeviceInventory.values()) {
-                pw.println("\t" + device + "\n");
-            }
-        }
     }
 
     //------------------------------------------------------------
@@ -725,8 +645,8 @@
     }
 
     /*package*/ void registerStrategyPreferredDevicesDispatcher(
-            @NonNull IStrategyPreferredDevicesDispatcher dispatcher, boolean isPrivileged) {
-        mPrefDevDispatchers.register(dispatcher, isPrivileged);
+            @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
+        mPrefDevDispatchers.register(dispatcher);
     }
 
     /*package*/ void unregisterStrategyPreferredDevicesDispatcher(
@@ -760,8 +680,8 @@
     }
 
     /*package*/ void registerCapturePresetDevicesRoleDispatcher(
-            @NonNull ICapturePresetDevicesRoleDispatcher dispatcher, boolean isPrivileged) {
-        mDevRoleCapturePresetDispatchers.register(dispatcher, isPrivileged);
+            @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
+        mDevRoleCapturePresetDispatchers.register(dispatcher);
     }
 
     /*package*/ void unregisterCapturePresetDevicesRoleDispatcher(
@@ -839,9 +759,6 @@
                 mConnectedDevices.put(deviceKey, new DeviceInfo(
                         device, deviceName, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
                 mDeviceBroker.postAccessoryPlugMediaUnmute(device);
-                if (AudioSystem.isBluetoothScoDevice(device)) {
-                    addAudioDeviceInInventoryIfNeeded(attributes);
-                }
                 mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
                 return true;
             } else if (!connect && isConnected) {
@@ -1071,9 +988,8 @@
         mDeviceBroker.setBluetoothA2dpOnInt(true, true /*fromA2dp*/, eventSource);
         // at this point there could be another A2DP device already connected in APM, but it
         // doesn't matter as this new one will overwrite the previous one
-        AudioDeviceAttributes ada = new AudioDeviceAttributes(
-                AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name);
-        final int res = mAudioSystem.setDeviceConnectionState(ada,
+        final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+                AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name),
                 AudioSystem.DEVICE_STATE_AVAILABLE, a2dpCodec);
 
         // TODO: log in MediaMetrics once distinction between connection failure and
@@ -1095,7 +1011,8 @@
         // The convention for head tracking sensors associated with A2DP devices is to
         // use a UUID derived from the MAC address as follows:
         //   time_low = 0, time_mid = 0, time_hi = 0, clock_seq = 0, node = MAC Address
-        UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada);
+        UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(
+                new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
         final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
                 address, a2dpCodec, sensorUuid);
         final String diKey = di.getKey();
@@ -1106,10 +1023,8 @@
 
         mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
         setCurrentAudioRouteNameIfPossible(name, true /*fromA2dp*/);
-        addAudioDeviceInInventoryIfNeeded(ada);
     }
 
-
     @GuardedBy("mDevicesLock")
     private void makeA2dpDeviceUnavailableNow(String address, int a2dpCodec) {
         MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "a2dp." + address)
@@ -1203,9 +1118,9 @@
         final int hearingAidVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType,
                 AudioSystem.DEVICE_OUT_HEARING_AID);
         mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, streamType);
-        AudioDeviceAttributes ada = new AudioDeviceAttributes(
-                AudioSystem.DEVICE_OUT_HEARING_AID, address, name);
-        mAudioSystem.setDeviceConnectionState(ada,
+
+        mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+                AudioSystem.DEVICE_OUT_HEARING_AID, address, name),
                 AudioSystem.DEVICE_STATE_AVAILABLE,
                 AudioSystem.AUDIO_FORMAT_DEFAULT);
         mConnectedDevices.put(
@@ -1216,7 +1131,6 @@
         mDeviceBroker.postApplyVolumeOnDevice(streamType,
                 AudioSystem.DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable");
         setCurrentAudioRouteNameIfPossible(name, false /*fromA2dp*/);
-        addAudioDeviceInInventoryIfNeeded(ada);
         new MediaMetrics.Item(mMetricsId + "makeHearingAidDeviceAvailable")
                 .set(MediaMetrics.Property.ADDRESS, address != null ? address : "")
                 .set(MediaMetrics.Property.DEVICE,
@@ -1253,15 +1167,13 @@
              */
             mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
 
-            AudioDeviceAttributes ada = new AudioDeviceAttributes(device, address, name);
-            AudioSystem.setDeviceConnectionState(ada,
+            AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(device, address, name),
                     AudioSystem.DEVICE_STATE_AVAILABLE,
                     AudioSystem.AUDIO_FORMAT_DEFAULT);
             mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
                     new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
             mDeviceBroker.postAccessoryPlugMediaUnmute(device);
             setCurrentAudioRouteNameIfPossible(name, /*fromA2dp=*/false);
-            addAudioDeviceInInventoryIfNeeded(ada);
         }
 
         if (streamType == AudioSystem.STREAM_DEFAULT) {
@@ -1562,9 +1474,6 @@
         final int nbDispatchers = mPrefDevDispatchers.beginBroadcast();
         for (int i = 0; i < nbDispatchers; i++) {
             try {
-                if (!((Boolean) mPrefDevDispatchers.getBroadcastCookie(i))) {
-                    devices = mDeviceBroker.anonymizeAudioDeviceAttributesListUnchecked(devices);
-                }
                 mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDevicesChanged(
                         strategy, devices);
             } catch (RemoteException e) {
@@ -1578,9 +1487,6 @@
         final int nbDispatchers = mDevRoleCapturePresetDispatchers.beginBroadcast();
         for (int i = 0; i < nbDispatchers; ++i) {
             try {
-                if (!((Boolean) mDevRoleCapturePresetDispatchers.getBroadcastCookie(i))) {
-                    devices = mDeviceBroker.anonymizeAudioDeviceAttributesListUnchecked(devices);
-                }
                 mDevRoleCapturePresetDispatchers.getBroadcastItem(i).dispatchDevicesRoleChanged(
                         capturePreset, role, devices);
             } catch (RemoteException e) {
@@ -1600,42 +1506,6 @@
             return di.mSensorUuid;
         }
     }
-
-    /*package*/ String getDeviceSettings() {
-        int deviceCatalogSize = 0;
-        synchronized (mDeviceInventoryLock) {
-            deviceCatalogSize = mDeviceInventory.size();
-
-            final StringBuilder settingsBuilder = new StringBuilder(
-                deviceCatalogSize * AdiDeviceState.getPeristedMaxSize());
-
-            Iterator<AdiDeviceState> iterator = mDeviceInventory.values().iterator();
-            if (iterator.hasNext()) {
-                settingsBuilder.append(iterator.next().toPersistableString());
-            }
-            while (iterator.hasNext()) {
-                settingsBuilder.append(SETTING_DEVICE_SEPARATOR_CHAR);
-                settingsBuilder.append(iterator.next().toPersistableString());
-            }
-            return settingsBuilder.toString();
-        }
-    }
-
-    /*package*/ void setDeviceSettings(String settings) {
-        clearDeviceInventory();
-        String[] devSettings = TextUtils.split(Objects.requireNonNull(settings),
-                SETTING_DEVICE_SEPARATOR);
-        // small list, not worth overhead of Arrays.stream(devSettings)
-        for (String setting : devSettings) {
-            AdiDeviceState devState = AdiDeviceState.fromPersistedString(setting);
-            // Note if the device is not compatible with spatialization mode or the device
-            // type is not canonical, it will be ignored in {@link SpatializerHelper}.
-            if (devState != null) {
-                addDeviceStateToInventory(devState);
-            }
-        }
-    }
-
     //----------------------------------------------------------
     // For tests only
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 58b07e3..02648c4 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -341,6 +341,7 @@
     private static final int MSG_DISPATCH_AUDIO_MODE = 40;
     private static final int MSG_ROUTING_UPDATED = 41;
     private static final int MSG_INIT_HEADTRACKING_SENSORS = 42;
+    private static final int MSG_PERSIST_SPATIAL_AUDIO_DEVICE_SETTINGS = 43;
     private static final int MSG_ADD_ASSISTANT_SERVICE_UID = 44;
     private static final int MSG_REMOVE_ASSISTANT_SERVICE_UID = 45;
     private static final int MSG_UPDATE_ACTIVE_ASSISTANT_SERVICE_UID = 46;
@@ -952,8 +953,6 @@
 
         mPlatformType = AudioSystem.getPlatformType(context);
 
-        mDeviceBroker = new AudioDeviceBroker(mContext, this);
-
         mIsSingleVolume = AudioSystem.isSingleVolume(context);
 
         mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
@@ -966,7 +965,7 @@
 
         mSfxHelper = new SoundEffectsHelper(mContext);
 
-        mSpatializerHelper = new SpatializerHelper(this, mAudioSystem, mDeviceBroker);
+        mSpatializerHelper = new SpatializerHelper(this, mAudioSystem);
 
         mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
         mHasVibrator = mVibrator == null ? false : mVibrator.hasVibrator();
@@ -1102,6 +1101,8 @@
         mUseFixedVolume = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_useFixedVolume);
 
+        mDeviceBroker = new AudioDeviceBroker(mContext, this);
+
         mRecordMonitor = new RecordingActivityMonitor(mContext);
         mRecordMonitor.registerRecordingCallback(mVoiceRecordingActivityMonitor, true);
 
@@ -2638,11 +2639,8 @@
             return AudioSystem.ERROR;
         }
         enforceModifyAudioRoutingPermission();
-
-        devices = retrieveBluetoothAddresses(devices);
-
         final String logString = String.format(
-                "setPreferredDevicesForStrategy u/pid:%d/%d strat:%d dev:%s",
+                "setPreferredDeviceForStrategy u/pid:%d/%d strat:%d dev:%s",
                 Binder.getCallingUid(), Binder.getCallingPid(), strategy,
                 devices.stream().map(e -> e.toString()).collect(Collectors.joining(",")));
         sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
@@ -2690,7 +2688,7 @@
                     status, strategy));
             return new ArrayList<AudioDeviceAttributes>();
         } else {
-            return anonymizeAudioDeviceAttributesList(devices);
+            return devices;
         }
     }
 
@@ -2703,8 +2701,7 @@
             return;
         }
         enforceModifyAudioRoutingPermission();
-        mDeviceBroker.registerStrategyPreferredDevicesDispatcher(
-                dispatcher, isBluetoothPrividged());
+        mDeviceBroker.registerStrategyPreferredDevicesDispatcher(dispatcher);
     }
 
     /** @see AudioManager#removeOnPreferredDevicesForStrategyChangedListener(
@@ -2720,7 +2717,7 @@
     }
 
     /**
-     * @see AudioManager#setPreferredDevicesForCapturePreset(int, AudioDeviceAttributes)
+     * @see AudioManager#setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)
      */
     public int setPreferredDevicesForCapturePreset(
             int capturePreset, List<AudioDeviceAttributes> devices) {
@@ -2739,8 +2736,6 @@
             return AudioSystem.ERROR;
         }
 
-        devices = retrieveBluetoothAddresses(devices);
-
         final int status = mDeviceBroker.setPreferredDevicesForCapturePresetSync(
                 capturePreset, devices);
         if (status != AudioSystem.SUCCESS) {
@@ -2779,7 +2774,7 @@
                     status, capturePreset));
             return new ArrayList<AudioDeviceAttributes>();
         } else {
-            return anonymizeAudioDeviceAttributesList(devices);
+            return devices;
         }
     }
 
@@ -2793,8 +2788,7 @@
             return;
         }
         enforceModifyAudioRoutingPermission();
-        mDeviceBroker.registerCapturePresetDevicesRoleDispatcher(
-                dispatcher, isBluetoothPrividged());
+        mDeviceBroker.registerCapturePresetDevicesRoleDispatcher(dispatcher);
     }
 
     /**
@@ -2814,9 +2808,7 @@
     public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
             @NonNull AudioAttributes attributes) {
         enforceQueryStateOrModifyRoutingPermission();
-
-        return new ArrayList<AudioDeviceAttributes>(anonymizeAudioDeviceAttributesList(
-                getDevicesForAttributesInt(attributes, false /* forVolume */)));
+        return getDevicesForAttributesInt(attributes, false /* forVolume */);
     }
 
     /** @see AudioManager#getAudioDevicesForAttributes(AudioAttributes)
@@ -2826,8 +2818,7 @@
      */
     public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesUnprotected(
             @NonNull AudioAttributes attributes) {
-        return new ArrayList<AudioDeviceAttributes>(anonymizeAudioDeviceAttributesList(
-                getDevicesForAttributesInt(attributes, false /* forVolume */)));
+        return getDevicesForAttributesInt(attributes, false /* forVolume */);
     }
 
     /**
@@ -5934,10 +5925,6 @@
         }
     }
 
-    /*package*/ SettingsAdapter getSettings() {
-        return mSettings;
-    }
-
     ///////////////////////////////////////////////////////////////////////////
     // Internal methods
     ///////////////////////////////////////////////////////////////////////////
@@ -6649,9 +6636,6 @@
         // verify arguments
         Objects.requireNonNull(device);
         AudioManager.enforceValidVolumeBehavior(deviceVolumeBehavior);
-
-        device = retrieveBluetoothAddress(device);
-
         sVolumeLogger.log(new AudioEventLogger.StringEvent("setDeviceVolumeBehavior: dev:"
                 + AudioSystem.getOutputDeviceName(device.getInternalType()) + " addr:"
                 + device.getAddress() + " behavior:"
@@ -6728,8 +6712,6 @@
         // verify permissions
         enforceQueryStateOrModifyRoutingPermission();
 
-        device = retrieveBluetoothAddress(device);
-
         return getDeviceVolumeBehaviorInt(device);
     }
 
@@ -6806,9 +6788,6 @@
     public void setWiredDeviceConnectionState(AudioDeviceAttributes attributes,
             @ConnectionState int state, String caller) {
         enforceModifyAudioRoutingPermission();
-
-        attributes = retrieveBluetoothAddress(attributes);
-
         if (state != CONNECTION_STATE_CONNECTED
                 && state != CONNECTION_STATE_DISCONNECTED) {
             throw new IllegalArgumentException("Invalid state " + state);
@@ -6830,9 +6809,6 @@
             boolean connected) {
         Objects.requireNonNull(device);
         enforceModifyAudioRoutingPermission();
-
-        device = retrieveBluetoothAddress(device);
-
         mDeviceBroker.setTestDeviceConnectionState(device,
                 connected ? CONNECTION_STATE_CONNECTED : CONNECTION_STATE_DISCONNECTED);
         // simulate a routing update from native
@@ -8153,6 +8129,10 @@
                     mSpatializerHelper.onInitSensors();
                     break;
 
+                case MSG_PERSIST_SPATIAL_AUDIO_DEVICE_SETTINGS:
+                    onPersistSpatialAudioDeviceSettings();
+                    break;
+
                 case MSG_CHECK_MUSIC_ACTIVE:
                     onCheckMusicActive((String) msg.obj);
                     break;
@@ -8162,7 +8142,6 @@
                     onConfigureSafeVolume((msg.what == MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED),
                             (String) msg.obj);
                     break;
-
                 case MSG_PERSIST_SAFE_VOLUME_STATE:
                     onPersistSafeVolumeState(msg.arg1);
                     break;
@@ -9123,103 +9102,42 @@
     }
 
     void onInitSpatializer() {
-        mDeviceBroker.onReadAudioDeviceSettings();
+        final String settings = mSettings.getSecureStringForUser(mContentResolver,
+                Settings.Secure.SPATIAL_AUDIO_ENABLED, UserHandle.USER_CURRENT);
+        if (settings == null) {
+            Log.e(TAG, "error reading spatial audio device settings");
+        } else {
+            Log.v(TAG, "restoring spatial audio device settings: " + settings);
+            mSpatializerHelper.setSADeviceSettings(settings);
+        }
         mSpatializerHelper.init(/*effectExpected*/ mHasSpatializerEffect);
         mSpatializerHelper.setFeatureEnabled(mHasSpatializerEffect);
     }
 
-    private boolean isBluetoothPrividged() {
-        return PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.BLUETOOTH_CONNECT)
-                || Binder.getCallingUid() == Process.SYSTEM_UID;
-    }
-
-    List<AudioDeviceAttributes> retrieveBluetoothAddresses(List<AudioDeviceAttributes> devices) {
-        if (isBluetoothPrividged()) {
-            return devices;
-        }
-
-        List<AudioDeviceAttributes> checkedDevices = new ArrayList<AudioDeviceAttributes>();
-        for (AudioDeviceAttributes ada : devices) {
-            if (ada == null) {
-                continue;
-            }
-            checkedDevices.add(retrieveBluetoothAddressUncheked(ada));
-        }
-        return checkedDevices;
-    }
-
-    AudioDeviceAttributes retrieveBluetoothAddress(@NonNull AudioDeviceAttributes ada) {
-        if (isBluetoothPrividged()) {
-            return ada;
-        }
-        return retrieveBluetoothAddressUncheked(ada);
-    }
-
-    AudioDeviceAttributes retrieveBluetoothAddressUncheked(@NonNull AudioDeviceAttributes ada) {
-        Objects.requireNonNull(ada);
-        if (AudioSystem.isBluetoothDevice(ada.getInternalType())) {
-            String anonymizedAddress = anonymizeBluetoothAddress(ada.getAddress());
-            for (AdiDeviceState ads : mDeviceBroker.getImmutableDeviceInventory()) {
-                if (!(AudioSystem.isBluetoothDevice(ads.getInternalDeviceType())
-                        && (ada.getInternalType() == ads.getInternalDeviceType())
-                        && anonymizedAddress.equals(anonymizeBluetoothAddress(
-                                ads.getDeviceAddress())))) {
-                    continue;
-                }
-                ada.setAddress(ads.getDeviceAddress());
-                break;
-            }
-        }
-        return ada;
-    }
-
     /**
-     * Convert a Bluetooth MAC address to an anonymized one when exposed to a non privileged app
-     * Must match the implementation of BluetoothUtils.toAnonymizedAddress()
-     * @param address Mac address to be anonymized
-     * @return anonymized mac address
+     * post a message to persist the spatial audio device settings.
+     * Message is delayed by 1s on purpose in case of successive changes in quick succession (at
+     * init time for instance)
+     * Note this method is made public to work around a Mockito bug where it needs to be public
+     * in order to be mocked by a test a the same package
+     * (see https://code.google.com/archive/p/mockito/issues/127)
      */
-    static String anonymizeBluetoothAddress(String address) {
-        if (address == null || address.length() != "AA:BB:CC:DD:EE:FF".length()) {
-            return null;
-        }
-        return "XX:XX:XX:XX" + address.substring("XX:XX:XX:XX".length());
+    public void persistSpatialAudioDeviceSettings() {
+        sendMsg(mAudioHandler,
+                MSG_PERSIST_SPATIAL_AUDIO_DEVICE_SETTINGS,
+                SENDMSG_REPLACE, /*arg1*/ 0, /*arg2*/ 0, TAG,
+                /*delay*/ 1000);
     }
 
-    private List<AudioDeviceAttributes> anonymizeAudioDeviceAttributesList(
-                List<AudioDeviceAttributes> devices) {
-        if (isBluetoothPrividged()) {
-            return devices;
+    void onPersistSpatialAudioDeviceSettings() {
+        final String settings = mSpatializerHelper.getSADeviceSettings();
+        Log.v(TAG, "saving spatial audio device settings: " + settings);
+        boolean res = mSettings.putSecureStringForUser(mContentResolver,
+                Settings.Secure.SPATIAL_AUDIO_ENABLED,
+                settings, UserHandle.USER_CURRENT);
+        if (!res) {
+            Log.e(TAG, "error saving spatial audio device settings: " + settings);
         }
-        return anonymizeAudioDeviceAttributesListUnchecked(devices);
-    }
-
-    /* package */ List<AudioDeviceAttributes> anonymizeAudioDeviceAttributesListUnchecked(
-            List<AudioDeviceAttributes> devices) {
-        List<AudioDeviceAttributes> anonymizedDevices = new ArrayList<AudioDeviceAttributes>();
-        for (AudioDeviceAttributes ada : devices) {
-            anonymizedDevices.add(anonymizeAudioDeviceAttributesUnchecked(ada));
-        }
-        return anonymizedDevices;
-    }
-
-    private AudioDeviceAttributes anonymizeAudioDeviceAttributesUnchecked(
-            AudioDeviceAttributes ada) {
-        if (!AudioSystem.isBluetoothDevice(ada.getInternalType())) {
-            return ada;
-        }
-        AudioDeviceAttributes res = new AudioDeviceAttributes(ada);
-        res.setAddress(anonymizeBluetoothAddress(ada.getAddress()));
-        return res;
-    }
-
-    private AudioDeviceAttributes anonymizeAudioDeviceAttributes(AudioDeviceAttributes ada) {
-        if (isBluetoothPrividged()) {
-            return ada;
-        }
-
-        return anonymizeAudioDeviceAttributesUnchecked(ada);
     }
 
     //==========================================================================================
@@ -9249,16 +9167,13 @@
         Objects.requireNonNull(usages);
         Objects.requireNonNull(device);
         enforceModifyAudioRoutingPermission();
-
-        final AudioDeviceAttributes ada = retrieveBluetoothAddress(device);
-
         if (timeOutMs <= 0 || usages.length == 0) {
             throw new IllegalArgumentException("Invalid timeOutMs/usagesToMute");
         }
         Log.i(TAG, "muteAwaitConnection dev:" + device + " timeOutMs:" + timeOutMs
                 + " usages:" + Arrays.toString(usages));
 
-        if (mDeviceBroker.isDeviceConnected(ada)) {
+        if (mDeviceBroker.isDeviceConnected(device)) {
             // not throwing an exception as there could be a race between a connection (server-side,
             // notification of connection in flight) and a mute operation (client-side)
             Log.i(TAG, "muteAwaitConnection ignored, device (" + device + ") already connected");
@@ -9270,26 +9185,19 @@
                         + mMutingExpectedDevice);
                 throw new IllegalStateException("muteAwaitConnection already in progress");
             }
-            mMutingExpectedDevice = ada;
+            mMutingExpectedDevice = device;
             mMutedUsagesAwaitingConnection = usages;
-            mPlaybackMonitor.muteAwaitConnection(usages, ada, timeOutMs);
+            mPlaybackMonitor.muteAwaitConnection(usages, device, timeOutMs);
         }
-        dispatchMuteAwaitConnection((cb, isPrivileged) -> {
-            try {
-                AudioDeviceAttributes dev = ada;
-                if (!isPrivileged) {
-                    dev = anonymizeAudioDeviceAttributesUnchecked(ada);
-                }
-                cb.dispatchOnMutedUntilConnection(dev, usages);
-            } catch (RemoteException e) { }
-        });
+        dispatchMuteAwaitConnection(cb -> { try {
+            cb.dispatchOnMutedUntilConnection(device, usages); } catch (RemoteException e) { } });
     }
 
     /** @see AudioManager#getMutingExpectedDevice */
     public @Nullable AudioDeviceAttributes getMutingExpectedDevice() {
         enforceModifyAudioRoutingPermission();
         synchronized (mMuteAwaitConnectionLock) {
-            return anonymizeAudioDeviceAttributes(mMutingExpectedDevice);
+            return mMutingExpectedDevice;
         }
     }
 
@@ -9298,9 +9206,6 @@
     public void cancelMuteAwaitConnection(@NonNull AudioDeviceAttributes device) {
         Objects.requireNonNull(device);
         enforceModifyAudioRoutingPermission();
-
-        final AudioDeviceAttributes ada = retrieveBluetoothAddress(device);
-
         Log.i(TAG, "cancelMuteAwaitConnection for device:" + device);
         final int[] mutedUsages;
         synchronized (mMuteAwaitConnectionLock) {
@@ -9310,7 +9215,7 @@
                 Log.i(TAG, "cancelMuteAwaitConnection ignored, no expected device");
                 return;
             }
-            if (!ada.equalTypeAddress(mMutingExpectedDevice)) {
+            if (!device.equalTypeAddress(mMutingExpectedDevice)) {
                 Log.e(TAG, "cancelMuteAwaitConnection ignored, got " + device
                         + "] but expected device is" + mMutingExpectedDevice);
                 throw new IllegalStateException("cancelMuteAwaitConnection for wrong device");
@@ -9320,14 +9225,8 @@
             mMutedUsagesAwaitingConnection = null;
             mPlaybackMonitor.cancelMuteAwaitConnection("cancelMuteAwaitConnection dev:" + device);
         }
-        dispatchMuteAwaitConnection((cb, isPrivileged) -> {
-            try {
-                AudioDeviceAttributes dev = ada;
-                if (!isPrivileged) {
-                    dev = anonymizeAudioDeviceAttributesUnchecked(ada);
-                }
-                cb.dispatchOnUnmutedEvent(
-                        AudioManager.MuteAwaitConnectionCallback.EVENT_CANCEL, dev, mutedUsages);
+        dispatchMuteAwaitConnection(cb -> { try { cb.dispatchOnUnmutedEvent(
+                    AudioManager.MuteAwaitConnectionCallback.EVENT_CANCEL, device, mutedUsages);
             } catch (RemoteException e) { } });
     }
 
@@ -9339,7 +9238,7 @@
             boolean register) {
         enforceModifyAudioRoutingPermission();
         if (register) {
-            mMuteAwaitConnectionDispatchers.register(cb, isBluetoothPrividged());
+            mMuteAwaitConnectionDispatchers.register(cb);
         } else {
             mMuteAwaitConnectionDispatchers.unregister(cb);
         }
@@ -9363,14 +9262,8 @@
             mPlaybackMonitor.cancelMuteAwaitConnection(
                     "checkMuteAwaitConnection device " + device + " connected, unmuting");
         }
-        dispatchMuteAwaitConnection((cb, isPrivileged) -> {
-            try {
-                AudioDeviceAttributes ada = device;
-                if (!isPrivileged) {
-                    ada = anonymizeAudioDeviceAttributesUnchecked(device);
-                }
-                cb.dispatchOnUnmutedEvent(AudioManager.MuteAwaitConnectionCallback.EVENT_CONNECTION,
-                        ada, mutedUsages);
+        dispatchMuteAwaitConnection(cb -> { try { cb.dispatchOnUnmutedEvent(
+                AudioManager.MuteAwaitConnectionCallback.EVENT_CONNECTION, device, mutedUsages);
             } catch (RemoteException e) { } });
     }
 
@@ -9390,8 +9283,7 @@
             mMutingExpectedDevice = null;
             mMutedUsagesAwaitingConnection = null;
         }
-        dispatchMuteAwaitConnection((cb, isPrivileged) -> {
-            try {
+        dispatchMuteAwaitConnection(cb -> { try {
                 cb.dispatchOnUnmutedEvent(
                         AudioManager.MuteAwaitConnectionCallback.EVENT_TIMEOUT,
                         timedOutDevice, mutedUsages);
@@ -9399,14 +9291,13 @@
     }
 
     private void dispatchMuteAwaitConnection(
-            java.util.function.BiConsumer<IMuteAwaitConnectionCallback, Boolean> callback) {
+            java.util.function.Consumer<IMuteAwaitConnectionCallback> callback) {
         final int nbDispatchers = mMuteAwaitConnectionDispatchers.beginBroadcast();
         // lazy initialization as errors unlikely
         ArrayList<IMuteAwaitConnectionCallback> errorList = null;
         for (int i = 0; i < nbDispatchers; i++) {
             try {
-                callback.accept(mMuteAwaitConnectionDispatchers.getBroadcastItem(i),
-                        (Boolean) mMuteAwaitConnectionDispatchers.getBroadcastCookie(i));
+                callback.accept(mMuteAwaitConnectionDispatchers.getBroadcastItem(i));
             } catch (Exception e) {
                 if (errorList == null) {
                     errorList = new ArrayList<>(1);
@@ -11659,9 +11550,6 @@
             @NonNull AudioDeviceAttributes device, @IntRange(from = 0) long delayMillis) {
         Objects.requireNonNull(device, "device must not be null");
         enforceModifyAudioRoutingPermission();
-
-        device = retrieveBluetoothAddress(device);
-
         final String getterKey = "additional_output_device_delay="
                 + device.getInternalType() + "," + device.getAddress(); // "getter" key as an id.
         final String setterKey = getterKey + "," + delayMillis;     // append the delay for setter
@@ -11682,9 +11570,6 @@
     @IntRange(from = 0)
     public long getAdditionalOutputDeviceDelay(@NonNull AudioDeviceAttributes device) {
         Objects.requireNonNull(device, "device must not be null");
-
-        device = retrieveBluetoothAddress(device);
-
         final String key = "additional_output_device_delay";
         final String reply = AudioSystem.getParameters(
                 key + "=" + device.getInternalType() + "," + device.getAddress());
@@ -11712,9 +11597,6 @@
     @IntRange(from = 0)
     public long getMaxAdditionalOutputDeviceDelay(@NonNull AudioDeviceAttributes device) {
         Objects.requireNonNull(device, "device must not be null");
-
-        device = retrieveBluetoothAddress(device);
-
         final String key = "max_additional_output_device_delay";
         final String reply = AudioSystem.getParameters(
                 key + "=" + device.getInternalType() + "," + device.getAddress());
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 6972b1f..5b26672 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -16,8 +16,6 @@
 
 package com.android.server.audio;
 
-import static android.media.AudioSystem.isBluetoothDevice;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -52,6 +50,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
+import java.util.Objects;
 import java.util.UUID;
 
 /**
@@ -71,12 +70,11 @@
 
     private final @NonNull AudioSystemAdapter mASA;
     private final @NonNull AudioService mAudioService;
-    private final @NonNull AudioDeviceBroker mDeviceBroker;
     private @Nullable SensorManager mSensorManager;
 
     //------------------------------------------------------------
 
-    /*package*/ static final SparseIntArray SPAT_MODE_FOR_DEVICE_TYPE = new SparseIntArray(15) {
+    private static final SparseIntArray SPAT_MODE_FOR_DEVICE_TYPE = new SparseIntArray(15) {
         {
             append(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, SpatializationMode.SPATIALIZER_TRANSAURAL);
             append(AudioDeviceInfo.TYPE_WIRED_HEADSET, SpatializationMode.SPATIALIZER_BINAURAL);
@@ -98,6 +96,17 @@
         }
     };
 
+    private static final int[] WIRELESS_TYPES = { AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
+            AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
+            AudioDeviceInfo.TYPE_BLE_HEADSET,
+            AudioDeviceInfo.TYPE_BLE_SPEAKER,
+            AudioDeviceInfo.TYPE_BLE_BROADCAST
+    };
+
+    private static final int[] WIRELESS_SPEAKER_TYPES = {
+            AudioDeviceInfo.TYPE_BLE_SPEAKER,
+    };
+
     // Spatializer state machine
     private static final int STATE_UNINITIALIZED = 0;
     private static final int STATE_NOT_SUPPORTED = 1;
@@ -111,7 +120,6 @@
     /** current level as reported by native Spatializer in callback */
     private int mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
     private int mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
-
     private boolean mTransauralSupported = false;
     private boolean mBinauralSupported = false;
     private boolean mIsHeadTrackingSupported = false;
@@ -154,13 +162,17 @@
      */
     private final ArrayList<Integer> mSACapableDeviceTypes = new ArrayList<>(0);
 
+    /**
+     * List of devices where Spatial Audio is possible. Each device can be enabled or disabled
+     * (== user choice to use or not)
+     */
+    private final ArrayList<SADeviceState> mSADevices = new ArrayList<>(0);
+
     //------------------------------------------------------
     // initialization
-    SpatializerHelper(@NonNull AudioService mother, @NonNull AudioSystemAdapter asa,
-            @NonNull AudioDeviceBroker deviceBroker) {
+    SpatializerHelper(@NonNull AudioService mother, @NonNull AudioSystemAdapter asa) {
         mAudioService = mother;
         mASA = asa;
-        mDeviceBroker = deviceBroker;
     }
 
     synchronized void init(boolean effectExpected) {
@@ -266,14 +278,6 @@
                     mSACapableDeviceTypes.add(SPAT_MODE_FOR_DEVICE_TYPE.keyAt(i));
                 }
             }
-
-            // Log the saved device states that are compatible with SA
-            for (AdiDeviceState deviceState : mDeviceBroker.getImmutableDeviceInventory()) {
-                if (isSADevice(deviceState)) {
-                    logDeviceState(deviceState, "setSADeviceSettings");
-                }
-            }
-
             // for both transaural / binaural, we are not forcing enablement as the init() method
             // could have been called another time after boot in case of audioserver restart
             if (mTransauralSupported) {
@@ -317,7 +321,7 @@
         mState = STATE_UNINITIALIZED;
         mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
         mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
-        init(/*effectExpected=*/true);
+        init(true);
         setSpatializerEnabledInt(featureEnabled);
     }
 
@@ -349,7 +353,7 @@
                 DEFAULT_ATTRIBUTES, false /* forVolume */).toArray(ROUTING_DEVICES);
 
         // is media routed to a new device?
-        if (isBluetoothDevice(ROUTING_DEVICES[0].getInternalType())) {
+        if (isWireless(ROUTING_DEVICES[0].getType())) {
             addWirelessDeviceIfNew(ROUTING_DEVICES[0]);
         }
 
@@ -493,9 +497,10 @@
     synchronized @NonNull List<AudioDeviceAttributes> getCompatibleAudioDevices() {
         // build unionOf(mCompatibleAudioDevices, mEnabledDevice) - mDisabledAudioDevices
         ArrayList<AudioDeviceAttributes> compatList = new ArrayList<>();
-        for (AdiDeviceState deviceState : mDeviceBroker.getImmutableDeviceInventory()) {
-            if (deviceState.isSAEnabled() && isSADevice(deviceState)) {
-                compatList.add(deviceState.getAudioDeviceAttributes());
+        for (SADeviceState dev : mSADevices) {
+            if (dev.mEnabled) {
+                compatList.add(new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
+                        dev.mDeviceType, dev.mDeviceAddress == null ? "" : dev.mDeviceAddress));
             }
         }
         return compatList;
@@ -517,41 +522,34 @@
     private void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada,
             boolean forceEnable) {
         loglogi("addCompatibleAudioDevice: dev=" + ada);
-        final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
-        initSAState(deviceState);
-        AdiDeviceState updatedDevice = null; // non-null on update.
-        if (deviceState != null) {
-            if (forceEnable && !deviceState.isSAEnabled()) {
-                updatedDevice = deviceState;
-                updatedDevice.setSAEnabled(true);
-            }
-        } else {
-            // When adding, force the device type to be a canonical one.
-            final int canonicalDeviceType = getCanonicalDeviceType(ada.getType(),
-                    ada.getInternalType());
-            if (canonicalDeviceType == AudioDeviceInfo.TYPE_UNKNOWN) {
-                Log.e(TAG, "addCompatibleAudioDevice with incompatible AudioDeviceAttributes "
-                        + ada);
-                return;
-            }
-            updatedDevice = new AdiDeviceState(canonicalDeviceType, ada.getInternalType(),
-                    ada.getAddress());
-            initSAState(updatedDevice);
-            mDeviceBroker.addDeviceStateToInventory(updatedDevice);
-        }
-        if (updatedDevice != null) {
-            onRoutingUpdated();
-            mDeviceBroker.persistAudioDeviceSettings();
-            logDeviceState(updatedDevice, "addCompatibleAudioDevice");
-        }
-    }
+        final int deviceType = ada.getType();
+        final boolean wireless = isWireless(deviceType);
+        boolean isInList = false;
+        SADeviceState deviceUpdated = null; // non-null on update.
 
-    private void initSAState(AdiDeviceState device) {
-        if (device == null) {
-            return;
+        for (SADeviceState deviceState : mSADevices) {
+            if (deviceType == deviceState.mDeviceType
+                    && (!wireless || ada.getAddress().equals(deviceState.mDeviceAddress))) {
+                isInList = true;
+                if (forceEnable) {
+                    deviceState.mEnabled = true;
+                    deviceUpdated = deviceState;
+                }
+                break;
+            }
         }
-        device.setSAEnabled(true);
-        device.setHeadTrackerEnabled(true);
+        if (!isInList) {
+            final SADeviceState dev = new SADeviceState(deviceType,
+                    wireless ? ada.getAddress() : "");
+            dev.mEnabled = true;
+            mSADevices.add(dev);
+            deviceUpdated = dev;
+        }
+        if (deviceUpdated != null) {
+            onRoutingUpdated();
+            mAudioService.persistSpatialAudioDeviceSettings();
+            logDeviceState(deviceUpdated, "addCompatibleAudioDevice");
+        }
     }
 
     private static final String METRICS_DEVICE_PREFIX = "audio.spatializer.device.";
@@ -561,60 +559,41 @@
     //
     // There may be different devices with the same device type (aliasing).
     // We always send the full device state info on each change.
-    static void logDeviceState(AdiDeviceState deviceState, String event) {
-        final String deviceName = AudioSystem.getDeviceName(deviceState.getInternalDeviceType());
+    private void logDeviceState(SADeviceState deviceState, String event) {
+        final String deviceName = AudioSystem.getDeviceName(deviceState.mDeviceType);
         new MediaMetrics.Item(METRICS_DEVICE_PREFIX + deviceName)
-                .set(MediaMetrics.Property.ADDRESS, deviceState.getDeviceAddress())
-                .set(MediaMetrics.Property.ENABLED, deviceState.isSAEnabled() ? "true" : "false")
-                .set(MediaMetrics.Property.EVENT, TextUtils.emptyIfNull(event))
-                .set(MediaMetrics.Property.HAS_HEAD_TRACKER,
-                        deviceState.hasHeadTracker() ? "true"
-                                : "false") // this may be updated later.
-                .set(MediaMetrics.Property.HEAD_TRACKER_ENABLED,
-                        deviceState.isHeadTrackerEnabled() ? "true" : "false")
-                .record();
+            .set(MediaMetrics.Property.ADDRESS, deviceState.mDeviceAddress)
+            .set(MediaMetrics.Property.ENABLED, deviceState.mEnabled ? "true" : "false")
+            .set(MediaMetrics.Property.EVENT, TextUtils.emptyIfNull(event))
+            .set(MediaMetrics.Property.HAS_HEAD_TRACKER,
+                    deviceState.mHasHeadTracker ? "true" : "false") // this may be updated later.
+            .set(MediaMetrics.Property.HEAD_TRACKER_ENABLED,
+                    deviceState.mHeadTrackerEnabled ? "true" : "false")
+            .record();
     }
 
     synchronized void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
         loglogi("removeCompatibleAudioDevice: dev=" + ada);
+        final int deviceType = ada.getType();
+        final boolean wireless = isWireless(deviceType);
+        SADeviceState deviceUpdated = null; // non-null on update.
 
-        final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
-        if (deviceState != null && deviceState.isSAEnabled()) {
-            deviceState.setSAEnabled(false);
+        for (SADeviceState deviceState : mSADevices) {
+            if (deviceType == deviceState.mDeviceType
+                    && (!wireless || ada.getAddress().equals(deviceState.mDeviceAddress))) {
+                deviceState.mEnabled = false;
+                deviceUpdated = deviceState;
+                break;
+            }
+        }
+        if (deviceUpdated != null) {
             onRoutingUpdated();
-            mDeviceBroker.persistAudioDeviceSettings();
-            logDeviceState(deviceState, "removeCompatibleAudioDevice");
+            mAudioService.persistSpatialAudioDeviceSettings();
+            logDeviceState(deviceUpdated, "removeCompatibleAudioDevice");
         }
     }
 
     /**
-     * Returns a possibly aliased device type which is used
-     * for spatial audio settings (or TYPE_UNKNOWN  if it doesn't exist).
-     */
-    @AudioDeviceInfo.AudioDeviceType
-    private static int getCanonicalDeviceType(int deviceType, int internalDeviceType) {
-        if (isBluetoothDevice(internalDeviceType)) return deviceType;
-
-        final int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(deviceType, Integer.MIN_VALUE);
-        if (spatMode == SpatializationMode.SPATIALIZER_TRANSAURAL) {
-            return AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
-        } else if (spatMode == SpatializationMode.SPATIALIZER_BINAURAL) {
-            return AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
-        }
-        return AudioDeviceInfo.TYPE_UNKNOWN;
-    }
-
-    /**
-     * Returns the Spatial Audio device state for an audio device attributes
-     * or null if it does not exist.
-     */
-    @Nullable
-    private AdiDeviceState findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada) {
-        return mDeviceBroker.findDeviceStateForAudioDeviceAttributes(ada,
-                getCanonicalDeviceType(ada.getType(), ada.getInternalType()));
-    }
-
-    /**
      * Return if Spatial Audio is enabled and available for the given device
      * @param ada
      * @return a pair of boolean, 1/ enabled? 2/ available?
@@ -623,7 +602,7 @@
         // if not a wireless device, this value will be overwritten to map the type
         // to TYPE_BUILTIN_SPEAKER or TYPE_WIRED_HEADPHONES
         @AudioDeviceInfo.AudioDeviceType int deviceType = ada.getType();
-        final boolean wireless = isBluetoothDevice(ada.getInternalType());
+        final boolean wireless = isWireless(deviceType);
 
         // if not a wireless device: find if media device is in the speaker, wired headphones
         if (!wireless) {
@@ -646,8 +625,7 @@
                 deviceType = AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
             }
         } else { // wireless device
-            if (ada.getInternalType() == AudioSystem.DEVICE_OUT_BLE_SPEAKER
-                    && !mTransauralSupported) {
+            if (isWirelessSpeaker(deviceType) && !mTransauralSupported) {
                 Log.i(TAG, "Device incompatible with Spatial Audio (no transaural) dev:"
                         + ada);
                 return new Pair<>(false, false);
@@ -659,35 +637,34 @@
             }
         }
 
-        final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
-        if (deviceState == null) {
-            // no matching device state?
-            Log.i(TAG, "no spatialization device state found for Spatial Audio device:" + ada);
-            return new Pair<>(false, false);
+        boolean enabled = false;
+        boolean available = false;
+        for (SADeviceState deviceState : mSADevices) {
+            if (deviceType == deviceState.mDeviceType
+                    && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+                    || !wireless) {
+                available = true;
+                enabled = deviceState.mEnabled;
+                break;
+            }
         }
-        // found the matching device state.
-        return new Pair<>(deviceState.isSAEnabled(), true);
+        return new Pair<>(enabled, available);
     }
 
     private synchronized void addWirelessDeviceIfNew(@NonNull AudioDeviceAttributes ada) {
-        if (!isDeviceCompatibleWithSpatializationModes(ada)) {
-            return;
-        }
-        if (findDeviceStateForAudioDeviceAttributes(ada) == null) {
-            // wireless device types should be canonical, but we translate to be sure.
-            final int canonicalDeviceType = getCanonicalDeviceType(ada.getType(),
-                    ada.getInternalType());
-            if (canonicalDeviceType == AudioDeviceInfo.TYPE_UNKNOWN) {
-                Log.e(TAG, "addWirelessDeviceIfNew with incompatible AudioDeviceAttributes "
-                        + ada);
-                return;
+        boolean knownDevice = false;
+        for (SADeviceState deviceState : mSADevices) {
+            // wireless device so always check address
+            if (ada.getType() == deviceState.mDeviceType
+                    && ada.getAddress().equals(deviceState.mDeviceAddress)) {
+                knownDevice = true;
+                break;
             }
-            final AdiDeviceState deviceState =
-                    new AdiDeviceState(canonicalDeviceType, ada.getInternalType(),
-                            ada.getAddress());
-            initSAState(deviceState);
-            mDeviceBroker.addDeviceStateToInventory(deviceState);
-            mDeviceBroker.persistAudioDeviceSettings();
+        }
+        if (!knownDevice) {
+            final SADeviceState deviceState = new SADeviceState(ada.getType(), ada.getAddress());
+            mSADevices.add(deviceState);
+            mAudioService.persistSpatialAudioDeviceSettings();
             logDeviceState(deviceState, "addWirelessDeviceIfNew"); // may be updated later.
         }
     }
@@ -728,7 +705,16 @@
             return false;
         }
 
-        return findDeviceStateForAudioDeviceAttributes(ada) != null;
+        final int deviceType = ada.getType();
+        final boolean wireless = isWireless(deviceType);
+        for (SADeviceState deviceState : mSADevices) {
+            if (deviceType == deviceState.mDeviceType
+                    && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+                    || !wireless) {
+                return true;
+            }
+        }
+        return false;
     }
 
     private synchronized boolean canBeSpatializedOnDevice(@NonNull AudioAttributes attributes,
@@ -743,26 +729,6 @@
         return false;
     }
 
-    private boolean isSADevice(AdiDeviceState deviceState) {
-        return deviceState.getDeviceType() == getCanonicalDeviceType(deviceState.getDeviceType(),
-                deviceState.getInternalDeviceType()) && isDeviceCompatibleWithSpatializationModes(
-                deviceState.getAudioDeviceAttributes());
-    }
-
-    private boolean isDeviceCompatibleWithSpatializationModes(@NonNull AudioDeviceAttributes ada) {
-        // modeForDevice will be neither transaural or binaural for devices that do not support
-        // spatial audio. For instance mono devices like earpiece, speaker safe or sco must
-        // not be included.
-        final byte modeForDevice = (byte) SPAT_MODE_FOR_DEVICE_TYPE.get(ada.getType(),
-            /*default when type not found*/ -1);
-        if ((modeForDevice == SpatializationMode.SPATIALIZER_BINAURAL && mBinauralSupported)
-                || (modeForDevice == SpatializationMode.SPATIALIZER_TRANSAURAL
-                    && mTransauralSupported)) {
-            return true;
-        }
-        return false;
-    }
-
     synchronized void setFeatureEnabled(boolean enabled) {
         loglogi("setFeatureEnabled(" + enabled + ") was featureEnabled:" + mFeatureEnabled);
         if (mFeatureEnabled == enabled) {
@@ -1123,20 +1089,27 @@
             Log.v(TAG, "no headtracking support, ignoring setHeadTrackerEnabled to " + enabled
                     + " for " + ada);
         }
-        final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
-        if (deviceState == null) return;
-        if (!deviceState.hasHeadTracker()) {
-            Log.e(TAG, "Called setHeadTrackerEnabled enabled:" + enabled
-                    + " device:" + ada + " on a device without headtracker");
-            return;
-        }
-        Log.i(TAG, "setHeadTrackerEnabled enabled:" + enabled + " device:" + ada);
-        deviceState.setHeadTrackerEnabled(enabled);
-        mDeviceBroker.persistAudioDeviceSettings();
-        logDeviceState(deviceState, "setHeadTrackerEnabled");
+        final int deviceType = ada.getType();
+        final boolean wireless = isWireless(deviceType);
 
+        for (SADeviceState deviceState : mSADevices) {
+            if (deviceType == deviceState.mDeviceType
+                    && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+                    || !wireless) {
+                if (!deviceState.mHasHeadTracker) {
+                    Log.e(TAG, "Called setHeadTrackerEnabled enabled:" + enabled
+                            + " device:" + ada + " on a device without headtracker");
+                    return;
+                }
+                Log.i(TAG, "setHeadTrackerEnabled enabled:" + enabled + " device:" + ada);
+                deviceState.mHeadTrackerEnabled = enabled;
+                mAudioService.persistSpatialAudioDeviceSettings();
+                logDeviceState(deviceState, "setHeadTrackerEnabled");
+                break;
+            }
+        }
         // check current routing to see if it affects the headtracking mode
-        if (ROUTING_DEVICES[0].getType() == ada.getType()
+        if (ROUTING_DEVICES[0].getType() == deviceType
                 && ROUTING_DEVICES[0].getAddress().equals(ada.getAddress())) {
             setDesiredHeadTrackingMode(enabled ? mDesiredHeadTrackingModeWhenEnabled
                     : Spatializer.HEAD_TRACKING_MODE_DISABLED);
@@ -1148,8 +1121,17 @@
             Log.v(TAG, "no headtracking support, hasHeadTracker always false for " + ada);
             return false;
         }
-        final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
-        return deviceState != null && deviceState.hasHeadTracker();
+        final int deviceType = ada.getType();
+        final boolean wireless = isWireless(deviceType);
+
+        for (SADeviceState deviceState : mSADevices) {
+            if (deviceType == deviceState.mDeviceType
+                    && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+                    || !wireless) {
+                return deviceState.mHasHeadTracker;
+            }
+        }
+        return false;
     }
 
     /**
@@ -1162,14 +1144,20 @@
             Log.v(TAG, "no headtracking support, setHasHeadTracker always false for " + ada);
             return false;
         }
-        final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
-        if (deviceState != null) {
-            if (!deviceState.hasHeadTracker()) {
-                deviceState.setHasHeadTracker(true);
-                mDeviceBroker.persistAudioDeviceSettings();
-                logDeviceState(deviceState, "setHasHeadTracker");
+        final int deviceType = ada.getType();
+        final boolean wireless = isWireless(deviceType);
+
+        for (SADeviceState deviceState : mSADevices) {
+            if (deviceType == deviceState.mDeviceType
+                    && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+                    || !wireless) {
+                if (!deviceState.mHasHeadTracker) {
+                    deviceState.mHasHeadTracker = true;
+                    mAudioService.persistSpatialAudioDeviceSettings();
+                    logDeviceState(deviceState, "setHasHeadTracker");
+                }
+                return deviceState.mHeadTrackerEnabled;
             }
-            return deviceState.isHeadTrackerEnabled();
         }
         Log.e(TAG, "setHasHeadTracker: device not found for:" + ada);
         return false;
@@ -1180,9 +1168,20 @@
             Log.v(TAG, "no headtracking support, isHeadTrackerEnabled always false for " + ada);
             return false;
         }
-        final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
-        return deviceState != null
-                && deviceState.hasHeadTracker() && deviceState.isHeadTrackerEnabled();
+        final int deviceType = ada.getType();
+        final boolean wireless = isWireless(deviceType);
+
+        for (SADeviceState deviceState : mSADevices) {
+            if (deviceType == deviceState.mDeviceType
+                    && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
+                    || !wireless) {
+                if (!deviceState.mHasHeadTracker) {
+                    return false;
+                }
+                return deviceState.mHeadTrackerEnabled;
+            }
+        }
+        return false;
     }
 
     synchronized boolean isHeadTrackerAvailable() {
@@ -1514,6 +1513,117 @@
         pw.println("\tsupports binaural:" + mBinauralSupported + " / transaural:"
                 + mTransauralSupported);
         pw.println("\tmSpatOutput:" + mSpatOutput);
+        pw.println("\tdevices:");
+        for (SADeviceState device : mSADevices) {
+            pw.println("\t\t" + device);
+        }
+    }
+
+    /*package*/ static final class SADeviceState {
+        final @AudioDeviceInfo.AudioDeviceType int mDeviceType;
+        final @NonNull String mDeviceAddress;
+        boolean mEnabled = true;               // by default, SA is enabled on any device
+        boolean mHasHeadTracker = false;
+        boolean mHeadTrackerEnabled = true;    // by default, if head tracker is present, use it
+        static final String SETTING_FIELD_SEPARATOR = ",";
+        static final String SETTING_DEVICE_SEPARATOR_CHAR = "|";
+        static final String SETTING_DEVICE_SEPARATOR = "\\|";
+
+        SADeviceState(@AudioDeviceInfo.AudioDeviceType int deviceType, @NonNull String address) {
+            mDeviceType = deviceType;
+            mDeviceAddress = Objects.requireNonNull(address);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null) {
+                return false;
+            }
+            // type check and cast
+            if (getClass() != obj.getClass()) {
+                return false;
+            }
+            final SADeviceState sads = (SADeviceState) obj;
+            return mDeviceType == sads.mDeviceType
+                    && mDeviceAddress.equals(sads.mDeviceAddress)
+                    && mEnabled == sads.mEnabled
+                    && mHasHeadTracker == sads.mHasHeadTracker
+                    && mHeadTrackerEnabled == sads.mHeadTrackerEnabled;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mDeviceType, mDeviceAddress, mEnabled, mHasHeadTracker,
+                    mHeadTrackerEnabled);
+        }
+
+        @Override
+        public String toString() {
+            return "type:" + mDeviceType + " addr:" + mDeviceAddress + " enabled:" + mEnabled
+                    + " HT:" + mHasHeadTracker + " HTenabled:" + mHeadTrackerEnabled;
+        }
+
+        String toPersistableString() {
+            return (new StringBuilder().append(mDeviceType)
+                    .append(SETTING_FIELD_SEPARATOR).append(mDeviceAddress)
+                    .append(SETTING_FIELD_SEPARATOR).append(mEnabled ? "1" : "0")
+                    .append(SETTING_FIELD_SEPARATOR).append(mHasHeadTracker ? "1" : "0")
+                    .append(SETTING_FIELD_SEPARATOR).append(mHeadTrackerEnabled ? "1" : "0")
+                    .toString());
+        }
+
+        static @Nullable SADeviceState fromPersistedString(@Nullable String persistedString) {
+            if (persistedString == null) {
+                return null;
+            }
+            if (persistedString.isEmpty()) {
+                return null;
+            }
+            String[] fields = TextUtils.split(persistedString, SETTING_FIELD_SEPARATOR);
+            if (fields.length != 5) {
+                // expecting all fields, fewer may mean corruption, ignore those settings
+                return null;
+            }
+            try {
+                final int deviceType = Integer.parseInt(fields[0]);
+                final SADeviceState deviceState = new SADeviceState(deviceType, fields[1]);
+                deviceState.mEnabled = Integer.parseInt(fields[2]) == 1;
+                deviceState.mHasHeadTracker = Integer.parseInt(fields[3]) == 1;
+                deviceState.mHeadTrackerEnabled = Integer.parseInt(fields[4]) == 1;
+                return deviceState;
+            } catch (NumberFormatException e) {
+                Log.e(TAG, "unable to parse setting for SADeviceState: " + persistedString, e);
+                return null;
+            }
+        }
+    }
+
+    /*package*/ synchronized String getSADeviceSettings() {
+        // expected max size of each String for each SADeviceState is 25 (accounting for separator)
+        final StringBuilder settingsBuilder = new StringBuilder(mSADevices.size() * 25);
+        for (int i = 0; i < mSADevices.size(); i++) {
+            settingsBuilder.append(mSADevices.get(i).toPersistableString());
+            if (i != mSADevices.size() - 1) {
+                settingsBuilder.append(SADeviceState.SETTING_DEVICE_SEPARATOR_CHAR);
+            }
+        }
+        return settingsBuilder.toString();
+    }
+
+    /*package*/ synchronized void setSADeviceSettings(@NonNull String persistedSettings) {
+        String[] devSettings = TextUtils.split(Objects.requireNonNull(persistedSettings),
+                SADeviceState.SETTING_DEVICE_SEPARATOR);
+        // small list, not worth overhead of Arrays.stream(devSettings)
+        for (String setting : devSettings) {
+            SADeviceState devState = SADeviceState.fromPersistedString(setting);
+            if (devState != null) {
+                mSADevices.add(devState);
+                logDeviceState(devState, "setSADeviceSettings");
+            }
+        }
     }
 
     private static String spatStateString(int state) {
@@ -1535,6 +1645,24 @@
         }
     }
 
+    private static boolean isWireless(@AudioDeviceInfo.AudioDeviceType int deviceType) {
+        for (int type : WIRELESS_TYPES) {
+            if (type == deviceType) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean isWirelessSpeaker(@AudioDeviceInfo.AudioDeviceType int deviceType) {
+        for (int type : WIRELESS_SPEAKER_TYPES) {
+            if (type == deviceType) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private int getHeadSensorHandleUpdateTracker() {
         int headHandle = -1;
         UUID routingDeviceUuid = mAudioService.getDeviceSensorUuid(ROUTING_DEVICES[0]);
@@ -1579,4 +1707,11 @@
         AudioService.sSpatialLogger.loglog(msg, AudioEventLogger.Event.ALOGE, TAG);
         return msg;
     }
+
+    //------------------------------------------------
+    // for testing purposes only
+
+    /*package*/ void clearSADevices() {
+        mSADevices.clear();
+    }
 }
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 73afa60..035c8ec 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -223,7 +223,8 @@
 
     /** Flags used when connecting to a sync adapter service */
     private static final int SYNC_ADAPTER_CONNECTION_FLAGS = Context.BIND_AUTO_CREATE
-            | Context.BIND_NOT_FOREGROUND | Context.BIND_ALLOW_OOM_MANAGEMENT;
+            | Context.BIND_NOT_FOREGROUND | Context.BIND_ALLOW_OOM_MANAGEMENT
+            | Context.BIND_DENY_ACTIVITY_STARTS_PRE_34;
 
     /** Singleton instance. */
     @GuardedBy("SyncManager.class")
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index c6f8975..cca12e72 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -238,7 +238,8 @@
             service.setComponent(mComponentName);
             try {
                 mBound = mContext.bindServiceAsUser(service, this,
-                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
+                                | Context.BIND_DENY_ACTIVITY_STARTS_PRE_34,
                         new UserHandle(mUserId));
                 if (!mBound && DEBUG) {
                     Slog.d(TAG, this + ": Bind failed");
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index f2834fd..f43f5f6 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -674,7 +674,7 @@
             if (bp == null) {
                 return;
             }
-            if (bp.isDynamic()) {
+            if (!bp.isDynamic()) {
                 // TODO: switch this back to SecurityException
                 Slog.wtf(TAG, "Not allowed to modify non-dynamic permission "
                         + permName);
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index f6748de..d05c0d6 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -80,6 +80,7 @@
 import android.window.SizeConfigurationBuckets;
 import android.window.TransitionInfo;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.AssistUtils;
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.protolog.common.ProtoLog;
@@ -88,6 +89,9 @@
 import com.android.server.pm.KnownPackages;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.uri.NeededUriGrants;
+import com.android.server.utils.quota.Categorizer;
+import com.android.server.utils.quota.Category;
+import com.android.server.utils.quota.CountQuotaTracker;
 import com.android.server.vr.VrManagerInternal;
 
 /**
@@ -103,6 +107,13 @@
     private final ActivityTaskSupervisor mTaskSupervisor;
     private final Context mContext;
 
+    // Prevent malicious app abusing the Activity#setPictureInPictureParams API
+    @VisibleForTesting CountQuotaTracker mSetPipAspectRatioQuotaTracker;
+    // Limit to 60 times / minute
+    private static final int SET_PIP_ASPECT_RATIO_LIMIT = 60;
+    // The timeWindowMs here can not be smaller than QuotaTracker#MIN_WINDOW_SIZE_MS
+    private static final long SET_PIP_ASPECT_RATIO_TIME_WINDOW_MS = 60_000;
+
     /** Wrapper around VoiceInteractionServiceManager. */
     private AssistUtils mAssistUtils;
 
@@ -734,6 +745,7 @@
     public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureParams params) {
         final long origId = Binder.clearCallingIdentity();
         try {
+            ensureSetPipAspectRatioQuotaTracker();
             synchronized (mGlobalLock) {
                 final ActivityRecord r = ensureValidPictureInPictureActivityParams(
                         "enterPictureInPictureMode", token, params);
@@ -748,6 +760,7 @@
     public void setPictureInPictureParams(IBinder token, final PictureInPictureParams params) {
         final long origId = Binder.clearCallingIdentity();
         try {
+            ensureSetPipAspectRatioQuotaTracker();
             synchronized (mGlobalLock) {
                 final ActivityRecord r = ensureValidPictureInPictureActivityParams(
                         "setPictureInPictureParams", token, params);
@@ -800,6 +813,19 @@
     }
 
     /**
+     * Initialize the {@link #mSetPipAspectRatioQuotaTracker} if applicable, which should happen
+     * out of {@link #mGlobalLock} to avoid deadlock (AM lock is used in QuotaTrack ctor).
+     */
+    private void ensureSetPipAspectRatioQuotaTracker() {
+        if (mSetPipAspectRatioQuotaTracker == null) {
+            mSetPipAspectRatioQuotaTracker = new CountQuotaTracker(mContext,
+                    Categorizer.SINGLE_CATEGORIZER);
+            mSetPipAspectRatioQuotaTracker.setCountLimit(Category.SINGLE_CATEGORY,
+                    SET_PIP_ASPECT_RATIO_LIMIT, SET_PIP_ASPECT_RATIO_TIME_WINDOW_MS);
+        }
+    }
+
+    /**
      * Checks the state of the system and the activity associated with the given {@param token} to
      * verify that picture-in-picture is supported for that activity.
      *
@@ -823,6 +849,19 @@
                     + ": Current activity does not support picture-in-picture.");
         }
 
+        // Rate limit how frequent an app can request aspect ratio change via
+        // Activity#setPictureInPictureParams
+        final int userId = UserHandle.getCallingUserId();
+        if (r.pictureInPictureArgs.hasSetAspectRatio()
+                && params.hasSetAspectRatio()
+                && !r.pictureInPictureArgs.getAspectRatio().equals(
+                params.getAspectRatio())
+                && !mSetPipAspectRatioQuotaTracker.noteEvent(
+                userId, r.packageName, "setPipAspectRatio")) {
+            throw new IllegalStateException(caller
+                    + ": Too many PiP aspect ratio change requests from " + r.packageName);
+        }
+
         final float minAspectRatio = mContext.getResources().getFloat(
                 com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
         final float maxAspectRatio = mContext.getResources().getFloat(
diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java
index 502cd2c..ea756c2 100644
--- a/services/print/java/com/android/server/print/RemotePrintService.java
+++ b/services/print/java/com/android/server/print/RemotePrintService.java
@@ -572,7 +572,8 @@
 
         boolean wasBound = mContext.bindServiceAsUser(mIntent, mServiceConnection,
                 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
-                        | Context.BIND_INCLUDE_CAPABILITIES | Context.BIND_ALLOW_INSTANT,
+                        | Context.BIND_INCLUDE_CAPABILITIES | Context.BIND_ALLOW_INSTANT
+                        | Context.BIND_DENY_ACTIVITY_STARTS_PRE_34,
                 new UserHandle(mUserId));
 
         if (!wasBound) {
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
index b6b987c..dad9fe8 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -29,14 +29,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.media.AudioDeviceAttributes;
-import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.media.BluetoothProfileConnectionInfo;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
-import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
@@ -55,6 +54,7 @@
     private static final String TAG = "AudioDeviceBrokerTest";
     private static final int MAX_MESSAGE_HANDLING_DELAY_MS = 100;
 
+    private Context mContext;
     // the actual class under test
     private AudioDeviceBroker mAudioDeviceBroker;
 
@@ -67,13 +67,13 @@
 
     @Before
     public void setUp() throws Exception {
-        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        mContext = InstrumentationRegistry.getTargetContext();
 
         mMockAudioService = mock(AudioService.class);
         mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
         mSpyDevInventory = spy(new AudioDeviceInventory(mSpyAudioSystem));
         mSpySystemServer = spy(new NoOpSystemServerAdapter());
-        mAudioDeviceBroker = new AudioDeviceBroker(context, mMockAudioService, mSpyDevInventory,
+        mAudioDeviceBroker = new AudioDeviceBroker(mContext, mMockAudioService, mSpyDevInventory,
                 mSpySystemServer);
         mSpyDevInventory.setDeviceBroker(mAudioDeviceBroker);
 
@@ -197,37 +197,6 @@
                 any(Intent.class));
     }
 
-    /**
-     * Test that constructing an AdiDeviceState instance requires a non-null address for a
-     * wireless type, but can take null for a non-wireless type;
-     * @throws Exception
-     */
-    @Test
-    public void testAdiDeviceStateNullAddressCtor() throws Exception {
-        try {
-            new AdiDeviceState(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
-                    AudioManager.DEVICE_OUT_SPEAKER, null);
-            new AdiDeviceState(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
-                    AudioManager.DEVICE_OUT_BLUETOOTH_A2DP, null);
-            Assert.fail();
-        } catch (NullPointerException e) { }
-    }
-
-    @Test
-    public void testAdiDeviceStateStringSerialization() throws Exception {
-        Log.i(TAG, "starting testAdiDeviceStateStringSerialization");
-        final AdiDeviceState devState = new AdiDeviceState(
-                AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, AudioManager.DEVICE_OUT_SPEAKER, "bla");
-        devState.setHasHeadTracker(false);
-        devState.setHeadTrackerEnabled(false);
-        devState.setSAEnabled(true);
-        final String persistString = devState.toPersistableString();
-        final AdiDeviceState result = AdiDeviceState.fromPersistedString(persistString);
-        Log.i(TAG, "original:" + devState);
-        Log.i(TAG, "result  :" + result);
-        Assert.assertEquals(devState, result);
-    }
-
     private void doTestConnectionDisconnectionReconnection(int delayAfterDisconnection,
             boolean mockMediaPlayback, boolean guaranteeSingleConnection) throws Exception {
         when(mMockAudioService.getDeviceForStream(AudioManager.STREAM_MUSIC))
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 bf38fd16..b17c3a1 100644
--- a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
@@ -15,19 +15,18 @@
  */
 package com.android.server.audio;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
+import com.android.server.audio.SpatializerHelper.SADeviceState;
+
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 
 import android.media.AudioDeviceAttributes;
-import android.media.AudioFormat;
+import android.media.AudioDeviceInfo;
 import android.media.AudioSystem;
 import android.util.Log;
 
 import androidx.test.filters.MediumTest;
-import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Assert;
@@ -50,23 +49,41 @@
 
     @Mock private AudioService mMockAudioService;
     @Spy private AudioSystemAdapter mSpyAudioSystem;
-    @Spy private AudioDeviceBroker mSpyDeviceBroker;
 
     @Before
     public void setUp() throws Exception {
         mMockAudioService = mock(AudioService.class);
-
         mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
-        mSpyDeviceBroker = spy(
-                new AudioDeviceBroker(
-                        InstrumentationRegistry.getInstrumentation().getTargetContext(),
-                        mMockAudioService));
-        mSpatHelper = new SpatializerHelper(mMockAudioService, mSpyAudioSystem,
-                mSpyDeviceBroker);
+
+        mSpatHelper = new SpatializerHelper(mMockAudioService, mSpyAudioSystem);
     }
 
     @Test
-    public void testAdiDeviceStateSettings() throws Exception {
+    public void testSADeviceStateNullAddressCtor() throws Exception {
+        try {
+            SADeviceState devState = new SADeviceState(
+                    AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, null);
+            Assert.fail();
+        } catch (NullPointerException e) { }
+    }
+
+    @Test
+    public void testSADeviceStateStringSerialization() throws Exception {
+        Log.i(TAG, "starting testSADeviceStateStringSerialization");
+        final SADeviceState devState = new SADeviceState(
+                AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, "bla");
+        devState.mHasHeadTracker = false;
+        devState.mHeadTrackerEnabled = false;
+        devState.mEnabled = true;
+        final String persistString = devState.toPersistableString();
+        final SADeviceState result = SADeviceState.fromPersistedString(persistString);
+        Log.i(TAG, "original:" + devState);
+        Log.i(TAG, "result  :" + result);
+        Assert.assertEquals(devState, result);
+    }
+
+    @Test
+    public void testSADeviceSettings() throws Exception {
         Log.i(TAG, "starting testSADeviceSettings");
         final AudioDeviceAttributes dev1 =
                 new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, "");
@@ -75,7 +92,7 @@
         final AudioDeviceAttributes dev3 =
                 new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "R2:D2:bloop");
 
-        doNothing().when(mSpyDeviceBroker).persistAudioDeviceSettings();
+        doNothing().when(mMockAudioService).persistSpatialAudioDeviceSettings();
 
         // test with single device
         mSpatHelper.addCompatibleAudioDevice(dev1);
@@ -109,11 +126,11 @@
      * the original one.
      */
     private void checkAddSettings() throws Exception {
-        String settings = mSpyDeviceBroker.getDeviceSettings();
+        String settings = mSpatHelper.getSADeviceSettings();
         Log.i(TAG, "device settings: " + settings);
-        mSpyDeviceBroker.clearDeviceInventory();
-        mSpyDeviceBroker.setDeviceSettings(settings);
-        String settingsRestored = mSpyDeviceBroker.getDeviceSettings();
+        mSpatHelper.clearSADevices();
+        mSpatHelper.setSADeviceSettings(settings);
+        String settingsRestored = mSpatHelper.getSADeviceSettings();
         Log.i(TAG, "device settingsRestored: " + settingsRestored);
         Assert.assertEquals(settings, settingsRestored);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 40ca250..223cc25 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -1058,6 +1058,12 @@
         assertNotNull(o.mInfo);
         assertNotNull(o.mInfo.pictureInPictureParams);
 
+        // Bypass the quota check, which causes NPE in current test setup.
+        if (mWm.mAtmService.mActivityClientController.mSetPipAspectRatioQuotaTracker != null) {
+            mWm.mAtmService.mActivityClientController.mSetPipAspectRatioQuotaTracker
+                    .setEnabled(false);
+        }
+
         final PictureInPictureParams p2 = new PictureInPictureParams.Builder()
                 .setAspectRatio(new Rational(3, 4)).build();
         mWm.mAtmService.mActivityClientController.setPictureInPictureParams(record.token, p2);
diff --git a/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java b/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java
index 55cbc72..99286f4 100644
--- a/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java
+++ b/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java
@@ -95,7 +95,7 @@
                 ITextToSpeechSessionCallback callback) {
             super(context,
                     new Intent(TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE).setPackage(engine),
-                    Context.BIND_AUTO_CREATE,
+                    Context.BIND_AUTO_CREATE | Context.BIND_DENY_ACTIVITY_STARTS_PRE_34,
                     userId,
                     ITextToSpeechService.Stub::asInterface);
             mEngine = engine;
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index bb0c4e9..9567405 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -16,6 +16,8 @@
 
 package com.android.server.usb;
 
+import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
+
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
 
 import android.annotation.NonNull;
@@ -42,6 +44,7 @@
 import android.os.Environment;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.Settings;
 import android.service.usb.UsbProfileGroupSettingsManagerProto;
 import android.service.usb.UsbSettingsAccessoryPreferenceProto;
 import android.service.usb.UsbSettingsDevicePreferenceProto;
@@ -913,10 +916,28 @@
             return;
         }
 
+        if (shouldRestrictOverlayActivities()) {
+            return;
+        }
+
         // Start activity with registered intent
         resolveActivity(intent, matches, defaultActivity, device, null);
     }
 
+    private boolean shouldRestrictOverlayActivities() {
+        if (Settings.Secure.getIntForUser(
+                mContext.getContentResolver(),
+                USER_SETUP_COMPLETE,
+                /* defaultValue= */ 1,
+                UserHandle.CURRENT.getIdentifier())
+                == 0) {
+            Slog.d(TAG, "restricting usb overlay activities as setup is not complete");
+            return true;
+        }
+
+        return false;
+    }
+
     public void deviceAttachedForFixedHandler(UsbDevice device, ComponentName component) {
         final Intent intent = createDeviceAttachedIntent(device);