summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-03-15 21:28:47 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-03-15 21:28:47 +0000
commit8af2c4d2fed0600c49af95c7e3b9c5462f709727 (patch)
treeba5c08e786ff79d51077ae7ef437098b8f2f434d
parent2744697978f00335f0674879c5f877c9c5c16ac6 (diff)
parenta25615aac07de47d62810966af925da48402e836 (diff)
downloadbase-android13-security-release.tar.gz
Merge cherrypicks of ['googleplex-android-review.googlesource.com/26599510', 'googleplex-android-review.googlesource.com/26599511'] into security-aosp-tm-release.android-security-13.0.0_r17android13-security-release
Change-Id: I1a04ce7b9e6d465f933deb45cecb72fcd0f1c48e
-rw-r--r--core/java/android/provider/Settings.java7
-rw-r--r--media/java/android/media/AudioDeviceAttributes.java26
-rw-r--r--media/java/android/media/AudioSystem.java63
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--services/core/java/com/android/server/audio/AdiDeviceState.java215
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java117
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java154
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java234
-rw-r--r--services/core/java/com/android/server/audio/SpatializerHelper.java471
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java39
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java53
11 files changed, 419 insertions, 961 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e8ed8b8a85d4..96cc1f892551 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9389,13 +9389,6 @@ public final class Settings {
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/media/java/android/media/AudioDeviceAttributes.java b/media/java/android/media/AudioDeviceAttributes.java
index 5a274353f68e..af3c295b8d6c 100644
--- a/media/java/android/media/AudioDeviceAttributes.java
+++ b/media/java/android/media/AudioDeviceAttributes.java
@@ -68,7 +68,7 @@ public final class AudioDeviceAttributes implements Parcelable {
/**
* 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 @@ public final class AudioDeviceAttributes implements Parcelable {
/**
* @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 @@ public final class AudioDeviceAttributes implements Parcelable {
/**
* @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 467464093e10..955bfcc902a4 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1208,9 +1208,6 @@ public class AudioSystem
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 @@ public class AudioSystem
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 da0689c6d177..cce515444c1f 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -687,7 +687,6 @@ public class SettingsBackupTest {
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/services/core/java/com/android/server/audio/AdiDeviceState.java b/services/core/java/com/android/server/audio/AdiDeviceState.java
deleted file mode 100644
index eab1eca90f6c..000000000000
--- 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 6ccdd827b45a..03dcc8d711d3 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.RemoteCallbackList;
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.UUID;
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 @@ public class AudioDeviceBroker {
}
/*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 @@ public class AudioDeviceBroker {
}
/*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 @@ public class AudioDeviceBroker {
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 @@ public class AudioDeviceBroker {
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 @@ public class AudioDeviceBroker {
// 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 @@ public class AudioDeviceBroker {
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 9524c54acc36..dbe4fb8c8795 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.text.TextUtils;
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 com.android.internal.annotations.VisibleForTesting;
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 @@ public class AudioDeviceInventory {
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 @@ public class AudioDeviceInventory {
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 @@ public class AudioDeviceInventory {
}
/*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 @@ public class AudioDeviceInventory {
}
/*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 @@ public class AudioDeviceInventory {
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 @@ public class AudioDeviceInventory {
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 @@ public class AudioDeviceInventory {
// 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 @@ public class AudioDeviceInventory {
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 @@ public class AudioDeviceInventory {
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 @@ public class AudioDeviceInventory {
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 @@ public class AudioDeviceInventory {
*/
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 @@ public class AudioDeviceInventory {
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 @@ public class AudioDeviceInventory {
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 @@ public class AudioDeviceInventory {
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 58b07e385ecf..02648c4da76f 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 @@ public class AudioService extends IAudioService.Stub
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 @@ public class AudioService extends IAudioService.Stub
mPlatformType = AudioSystem.getPlatformType(context);
- mDeviceBroker = new AudioDeviceBroker(mContext, this);
-
mIsSingleVolume = AudioSystem.isSingleVolume(context);
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
@@ -966,7 +965,7 @@ public class AudioService extends IAudioService.Stub
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 @@ public class AudioService extends IAudioService.Stub
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 @@ public class AudioService extends IAudioService.Stub
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 @@ public class AudioService extends IAudioService.Stub
status, strategy));
return new ArrayList<AudioDeviceAttributes>();
} else {
- return anonymizeAudioDeviceAttributesList(devices);
+ return devices;
}
}
@@ -2703,8 +2701,7 @@ public class AudioService extends IAudioService.Stub
return;
}
enforceModifyAudioRoutingPermission();
- mDeviceBroker.registerStrategyPreferredDevicesDispatcher(
- dispatcher, isBluetoothPrividged());
+ mDeviceBroker.registerStrategyPreferredDevicesDispatcher(dispatcher);
}
/** @see AudioManager#removeOnPreferredDevicesForStrategyChangedListener(
@@ -2720,7 +2717,7 @@ public class AudioService extends IAudioService.Stub
}
/**
- * @see AudioManager#setPreferredDevicesForCapturePreset(int, AudioDeviceAttributes)
+ * @see AudioManager#setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)
*/
public int setPreferredDevicesForCapturePreset(
int capturePreset, List<AudioDeviceAttributes> devices) {
@@ -2739,8 +2736,6 @@ public class AudioService extends IAudioService.Stub
return AudioSystem.ERROR;
}
- devices = retrieveBluetoothAddresses(devices);
-
final int status = mDeviceBroker.setPreferredDevicesForCapturePresetSync(
capturePreset, devices);
if (status != AudioSystem.SUCCESS) {
@@ -2779,7 +2774,7 @@ public class AudioService extends IAudioService.Stub
status, capturePreset));
return new ArrayList<AudioDeviceAttributes>();
} else {
- return anonymizeAudioDeviceAttributesList(devices);
+ return devices;
}
}
@@ -2793,8 +2788,7 @@ public class AudioService extends IAudioService.Stub
return;
}
enforceModifyAudioRoutingPermission();
- mDeviceBroker.registerCapturePresetDevicesRoleDispatcher(
- dispatcher, isBluetoothPrividged());
+ mDeviceBroker.registerCapturePresetDevicesRoleDispatcher(dispatcher);
}
/**
@@ -2814,9 +2808,7 @@ public class AudioService extends IAudioService.Stub
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 class AudioService extends IAudioService.Stub
*/
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 @@ public class AudioService extends IAudioService.Stub
}
}
- /*package*/ SettingsAdapter getSettings() {
- return mSettings;
- }
-
///////////////////////////////////////////////////////////////////////////
// Internal methods
///////////////////////////////////////////////////////////////////////////
@@ -6649,9 +6636,6 @@ public class AudioService extends IAudioService.Stub
// 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 @@ public class AudioService extends IAudioService.Stub
// verify permissions
enforceQueryStateOrModifyRoutingPermission();
- device = retrieveBluetoothAddress(device);
-
return getDeviceVolumeBehaviorInt(device);
}
@@ -6806,9 +6788,6 @@ public class AudioService extends IAudioService.Stub
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 @@ public class AudioService extends IAudioService.Stub
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 @@ public class AudioService extends IAudioService.Stub
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 @@ public class AudioService extends IAudioService.Stub
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 @@ public class AudioService extends IAudioService.Stub
}
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());
- }
-
- private List<AudioDeviceAttributes> anonymizeAudioDeviceAttributesList(
- List<AudioDeviceAttributes> devices) {
- if (isBluetoothPrividged()) {
- return devices;
- }
- 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;
+ public void persistSpatialAudioDeviceSettings() {
+ sendMsg(mAudioHandler,
+ MSG_PERSIST_SPATIAL_AUDIO_DEVICE_SETTINGS,
+ SENDMSG_REPLACE, /*arg1*/ 0, /*arg2*/ 0, TAG,
+ /*delay*/ 1000);
}
- private AudioDeviceAttributes anonymizeAudioDeviceAttributes(AudioDeviceAttributes ada) {
- if (isBluetoothPrividged()) {
- return ada;
+ 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 anonymizeAudioDeviceAttributesUnchecked(ada);
}
//==========================================================================================
@@ -9249,16 +9167,13 @@ public class AudioService extends IAudioService.Stub
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 @@ public class AudioService extends IAudioService.Stub
+ 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 class AudioService extends IAudioService.Stub
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 @@ public class AudioService extends IAudioService.Stub
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 @@ public class AudioService extends IAudioService.Stub
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 @@ public class AudioService extends IAudioService.Stub
boolean register) {
enforceModifyAudioRoutingPermission();
if (register) {
- mMuteAwaitConnectionDispatchers.register(cb, isBluetoothPrividged());
+ mMuteAwaitConnectionDispatchers.register(cb);
} else {
mMuteAwaitConnectionDispatchers.unregister(cb);
}
@@ -9363,14 +9262,8 @@ public class AudioService extends IAudioService.Stub
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 @@ public class AudioService extends IAudioService.Stub
mMutingExpectedDevice = null;
mMutedUsagesAwaitingConnection = null;
}
- dispatchMuteAwaitConnection((cb, isPrivileged) -> {
- try {
+ dispatchMuteAwaitConnection(cb -> { try {
cb.dispatchOnUnmutedEvent(
AudioManager.MuteAwaitConnectionCallback.EVENT_TIMEOUT,
timedOutDevice, mutedUsages);
@@ -9399,14 +9291,13 @@ public class AudioService extends IAudioService.Stub
}
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 @@ public class AudioService extends IAudioService.Stub
@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 @@ public class AudioService extends IAudioService.Stub
@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 @@ public class AudioService extends IAudioService.Stub
@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 6972b1f9b82b..5b26672c7de2 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.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.UUID;
/**
@@ -71,12 +70,11 @@ public class SpatializerHelper {
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 @@ public class SpatializerHelper {
}
};
+ 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 @@ public class SpatializerHelper {
/** 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 @@ public class SpatializerHelper {
*/
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 @@ public class SpatializerHelper {
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 @@ public class SpatializerHelper {
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 @@ public class SpatializerHelper {
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 @@ public class SpatializerHelper {
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 @@ public class SpatializerHelper {
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;
+ final int deviceType = ada.getType();
+ final boolean wireless = isWireless(deviceType);
+ boolean isInList = false;
+ SADeviceState deviceUpdated = null; // non-null on update.
+
+ for (SADeviceState deviceState : mSADevices) {
+ if (deviceType == deviceState.mDeviceType
+ && (!wireless || ada.getAddress().equals(deviceState.mDeviceAddress))) {
+ isInList = true;
+ if (forceEnable) {
+ deviceState.mEnabled = true;
+ deviceUpdated = deviceState;
+ }
+ break;
}
- updatedDevice = new AdiDeviceState(canonicalDeviceType, ada.getInternalType(),
- ada.getAddress());
- initSAState(updatedDevice);
- mDeviceBroker.addDeviceStateToInventory(updatedDevice);
}
- if (updatedDevice != null) {
- onRoutingUpdated();
- mDeviceBroker.persistAudioDeviceSettings();
- logDeviceState(updatedDevice, "addCompatibleAudioDevice");
+ if (!isInList) {
+ final SADeviceState dev = new SADeviceState(deviceType,
+ wireless ? ada.getAddress() : "");
+ dev.mEnabled = true;
+ mSADevices.add(dev);
+ deviceUpdated = dev;
}
- }
-
- private void initSAState(AdiDeviceState device) {
- if (device == null) {
- return;
+ if (deviceUpdated != null) {
+ onRoutingUpdated();
+ mAudioService.persistSpatialAudioDeviceSettings();
+ logDeviceState(deviceUpdated, "addCompatibleAudioDevice");
}
- device.setSAEnabled(true);
- device.setHeadTrackerEnabled(true);
}
private static final String METRICS_DEVICE_PREFIX = "audio.spatializer.device.";
@@ -561,57 +559,38 @@ public class SpatializerHelper {
//
// 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 AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
- if (deviceState != null && deviceState.isSAEnabled()) {
- deviceState.setSAEnabled(false);
- onRoutingUpdated();
- mDeviceBroker.persistAudioDeviceSettings();
- logDeviceState(deviceState, "removeCompatibleAudioDevice");
+ final int deviceType = ada.getType();
+ final boolean wireless = isWireless(deviceType);
+ SADeviceState deviceUpdated = null; // non-null on update.
+
+ for (SADeviceState deviceState : mSADevices) {
+ if (deviceType == deviceState.mDeviceType
+ && (!wireless || ada.getAddress().equals(deviceState.mDeviceAddress))) {
+ deviceState.mEnabled = false;
+ deviceUpdated = deviceState;
+ break;
+ }
}
- }
-
- /**
- * 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;
+ if (deviceUpdated != null) {
+ onRoutingUpdated();
+ mAudioService.persistSpatialAudioDeviceSettings();
+ logDeviceState(deviceUpdated, "removeCompatibleAudioDevice");
}
- 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()));
}
/**
@@ -623,7 +602,7 @@ public class SpatializerHelper {
// 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 @@ public class SpatializerHelper {
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 @@ public class SpatializerHelper {
}
}
- 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 @@ public class SpatializerHelper {
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 @@ public class SpatializerHelper {
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 @@ public class SpatializerHelper {
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 @@ public class SpatializerHelper {
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 @@ public class SpatializerHelper {
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 @@ public class SpatializerHelper {
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 @@ public class SpatializerHelper {
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 @@ public class SpatializerHelper {
}
}
+ 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 @@ public class SpatializerHelper {
AudioService.sSpatialLogger.loglog(msg, AudioEventLogger.Event.ALOGE, TAG);
return msg;
}
+
+ //------------------------------------------------
+ // for testing purposes only
+
+ /*package*/ void clearSADevices() {
+ mSADevices.clear();
+ }
}
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 b6b987c01a4a..dad9fe8648b2 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.bluetooth.BluetoothDevice;
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 @@ public class AudioDeviceBrokerTest {
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 @@ public class AudioDeviceBrokerTest {
@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 @@ public class AudioDeviceBrokerTest {
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 bf38fd1669ad..b17c3a18d89e 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 @@ public class SpatializerHelperTest {
@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 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 testAdiDeviceStateSettings() throws Exception {
+ public void testSADeviceSettings() throws Exception {
Log.i(TAG, "starting testSADeviceSettings");
final AudioDeviceAttributes dev1 =
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, "");
@@ -75,7 +92,7 @@ public class SpatializerHelperTest {
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 @@ public class SpatializerHelperTest {
* 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);
}