Merge "AudioService: persist/restore audio device spatial audio settings" into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
11782ee969
@@ -341,8 +341,7 @@ public class AudioService extends IAudioService.Stub
|
|||||||
private static final int MSG_DISPATCH_AUDIO_MODE = 40;
|
private static final int MSG_DISPATCH_AUDIO_MODE = 40;
|
||||||
private static final int MSG_ROUTING_UPDATED = 41;
|
private static final int MSG_ROUTING_UPDATED = 41;
|
||||||
private static final int MSG_INIT_HEADTRACKING_SENSORS = 42;
|
private static final int MSG_INIT_HEADTRACKING_SENSORS = 42;
|
||||||
// commented out for now, will be reused for other SA persisting
|
private static final int MSG_PERSIST_SPATIAL_AUDIO_DEVICE_SETTINGS = 43;
|
||||||
//private static final int MSG_PERSIST_SPATIAL_AUDIO_ENABLED = 43;
|
|
||||||
private static final int MSG_ADD_ASSISTANT_SERVICE_UID = 44;
|
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_REMOVE_ASSISTANT_SERVICE_UID = 45;
|
||||||
private static final int MSG_UPDATE_ACTIVE_ASSISTANT_SERVICE_UID = 46;
|
private static final int MSG_UPDATE_ACTIVE_ASSISTANT_SERVICE_UID = 46;
|
||||||
@@ -8122,8 +8121,7 @@ public class AudioService extends IAudioService.Stub
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case MSG_INIT_SPATIALIZER:
|
case MSG_INIT_SPATIALIZER:
|
||||||
mSpatializerHelper.init(/*effectExpected*/ mHasSpatializerEffect);
|
onInitSpatializer();
|
||||||
mSpatializerHelper.setFeatureEnabled(mHasSpatializerEffect);
|
|
||||||
mAudioEventWakeLock.release();
|
mAudioEventWakeLock.release();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -8131,6 +8129,10 @@ public class AudioService extends IAudioService.Stub
|
|||||||
mSpatializerHelper.onInitSensors();
|
mSpatializerHelper.onInitSensors();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MSG_PERSIST_SPATIAL_AUDIO_DEVICE_SETTINGS:
|
||||||
|
onPersistSpatialAudioDeviceSettings();
|
||||||
|
break;
|
||||||
|
|
||||||
case MSG_CHECK_MUSIC_ACTIVE:
|
case MSG_CHECK_MUSIC_ACTIVE:
|
||||||
onCheckMusicActive((String) msg.obj);
|
onCheckMusicActive((String) msg.obj);
|
||||||
break;
|
break;
|
||||||
@@ -9099,6 +9101,45 @@ public class AudioService extends IAudioService.Stub
|
|||||||
/*arg1*/ 0, /*arg2*/ 0, TAG, /*delay*/ 0);
|
/*arg1*/ 0, /*arg2*/ 0, TAG, /*delay*/ 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onInitSpatializer() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
*/
|
||||||
|
public void persistSpatialAudioDeviceSettings() {
|
||||||
|
sendMsg(mAudioHandler,
|
||||||
|
MSG_PERSIST_SPATIAL_AUDIO_DEVICE_SETTINGS,
|
||||||
|
SENDMSG_REPLACE, /*arg1*/ 0, /*arg2*/ 0, TAG,
|
||||||
|
/*delay*/ 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//==========================================================================================
|
//==========================================================================================
|
||||||
private boolean readCameraSoundForced() {
|
private boolean readCameraSoundForced() {
|
||||||
return SystemProperties.getBoolean("audio.camerasound.force", false) ||
|
return SystemProperties.getBoolean("audio.camerasound.force", false) ||
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import android.media.Spatializer;
|
|||||||
import android.media.SpatializerHeadTrackingMode;
|
import android.media.SpatializerHeadTrackingMode;
|
||||||
import android.os.RemoteCallbackList;
|
import android.os.RemoteCallbackList;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.util.SparseIntArray;
|
import android.util.SparseIntArray;
|
||||||
@@ -48,6 +49,7 @@ import java.io.PrintWriter;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -275,18 +277,17 @@ public class SpatializerHelper {
|
|||||||
// for both transaural / binaural, we are not forcing enablement as the init() method
|
// 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
|
// could have been called another time after boot in case of audioserver restart
|
||||||
if (mTransauralSupported) {
|
if (mTransauralSupported) {
|
||||||
// TODO deal with persisted values
|
// not force-enabling as this device might already be in the device list
|
||||||
addCompatibleAudioDevice(
|
addCompatibleAudioDevice(
|
||||||
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""),
|
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""),
|
||||||
false /*forceEnable*/);
|
false /*forceEnable*/);
|
||||||
}
|
}
|
||||||
if (mBinauralSupported) {
|
if (mBinauralSupported) {
|
||||||
// TODO deal with persisted values
|
// not force-enabling as this device might already be in the device list
|
||||||
addCompatibleAudioDevice(
|
addCompatibleAudioDevice(
|
||||||
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, ""),
|
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, ""),
|
||||||
false /*forceEnable*/);
|
false /*forceEnable*/);
|
||||||
}
|
}
|
||||||
// TODO read persisted states
|
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
resetCapabilities();
|
resetCapabilities();
|
||||||
} finally {
|
} finally {
|
||||||
@@ -533,11 +534,12 @@ public class SpatializerHelper {
|
|||||||
}
|
}
|
||||||
if (!isInList) {
|
if (!isInList) {
|
||||||
final SADeviceState dev = new SADeviceState(deviceType,
|
final SADeviceState dev = new SADeviceState(deviceType,
|
||||||
wireless ? ada.getAddress() : null);
|
wireless ? ada.getAddress() : "");
|
||||||
dev.mEnabled = true;
|
dev.mEnabled = true;
|
||||||
mSADevices.add(dev);
|
mSADevices.add(dev);
|
||||||
}
|
}
|
||||||
onRoutingUpdated();
|
onRoutingUpdated();
|
||||||
|
mAudioService.persistSpatialAudioDeviceSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
|
synchronized void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
|
||||||
@@ -553,6 +555,7 @@ public class SpatializerHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
onRoutingUpdated();
|
onRoutingUpdated();
|
||||||
|
mAudioService.persistSpatialAudioDeviceSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -625,7 +628,7 @@ public class SpatializerHelper {
|
|||||||
}
|
}
|
||||||
if (!knownDevice) {
|
if (!knownDevice) {
|
||||||
mSADevices.add(new SADeviceState(ada.getType(), ada.getAddress()));
|
mSADevices.add(new SADeviceState(ada.getType(), ada.getAddress()));
|
||||||
//### TODO persist list
|
mAudioService.persistSpatialAudioDeviceSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1059,6 +1062,7 @@ public class SpatializerHelper {
|
|||||||
}
|
}
|
||||||
Log.i(TAG, "setHeadTrackerEnabled enabled:" + enabled + " device:" + ada);
|
Log.i(TAG, "setHeadTrackerEnabled enabled:" + enabled + " device:" + ada);
|
||||||
deviceState.mHeadTrackerEnabled = enabled;
|
deviceState.mHeadTrackerEnabled = enabled;
|
||||||
|
mAudioService.persistSpatialAudioDeviceSettings();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1097,7 +1101,10 @@ public class SpatializerHelper {
|
|||||||
if (deviceType == deviceState.mDeviceType
|
if (deviceType == deviceState.mDeviceType
|
||||||
&& (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
|
&& (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
|
||||||
|| !wireless) {
|
|| !wireless) {
|
||||||
|
if (!deviceState.mHasHeadTracker) {
|
||||||
deviceState.mHasHeadTracker = true;
|
deviceState.mHasHeadTracker = true;
|
||||||
|
mAudioService.persistSpatialAudioDeviceSettings();
|
||||||
|
}
|
||||||
return deviceState.mHeadTrackerEnabled;
|
return deviceState.mHeadTrackerEnabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1457,16 +1464,45 @@ public class SpatializerHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class SADeviceState {
|
/*package*/ static final class SADeviceState {
|
||||||
final @AudioDeviceInfo.AudioDeviceType int mDeviceType;
|
final @AudioDeviceInfo.AudioDeviceType int mDeviceType;
|
||||||
final @Nullable String mDeviceAddress; // non-null for wireless devices
|
final @NonNull String mDeviceAddress;
|
||||||
boolean mEnabled = true; // by default, SA is enabled on any device
|
boolean mEnabled = true; // by default, SA is enabled on any device
|
||||||
boolean mHasHeadTracker = false;
|
boolean mHasHeadTracker = false;
|
||||||
boolean mHeadTrackerEnabled = true; // by default, if head tracker is present, use it
|
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, @Nullable String address) {
|
SADeviceState(@AudioDeviceInfo.AudioDeviceType int deviceType, @NonNull String address) {
|
||||||
mDeviceType = deviceType;
|
mDeviceType = deviceType;
|
||||||
mDeviceAddress = address;
|
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
|
@Override
|
||||||
@@ -1474,6 +1510,64 @@ public class SpatializerHelper {
|
|||||||
return "type:" + mDeviceType + " addr:" + mDeviceAddress + " enabled:" + mEnabled
|
return "type:" + mDeviceType + " addr:" + mDeviceAddress + " enabled:" + mEnabled
|
||||||
+ " HT:" + mHasHeadTracker + " HTenabled:" + mHeadTrackerEnabled;
|
+ " 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String spatStateString(int state) {
|
private static String spatStateString(int state) {
|
||||||
@@ -1557,4 +1651,11 @@ public class SpatializerHelper {
|
|||||||
AudioService.sSpatialLogger.loglog(msg, AudioEventLogger.Event.ALOGE, TAG);
|
AudioService.sSpatialLogger.loglog(msg, AudioEventLogger.Event.ALOGE, TAG);
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------
|
||||||
|
// for testing purposes only
|
||||||
|
|
||||||
|
/*package*/ void clearSADevices() {
|
||||||
|
mSADevices.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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 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.AudioDeviceInfo;
|
||||||
|
import android.media.AudioSystem;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.test.filters.MediumTest;
|
||||||
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Spy;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class SpatializerHelperTest {
|
||||||
|
|
||||||
|
private static final String TAG = "SpatializerHelperTest";
|
||||||
|
|
||||||
|
// the actual class under test
|
||||||
|
private SpatializerHelper mSpatHelper;
|
||||||
|
|
||||||
|
@Mock private AudioService mMockAudioService;
|
||||||
|
@Spy private AudioSystemAdapter mSpyAudioSystem;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
mMockAudioService = mock(AudioService.class);
|
||||||
|
mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
|
||||||
|
|
||||||
|
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 testSADeviceSettings() throws Exception {
|
||||||
|
Log.i(TAG, "starting testSADeviceSettings");
|
||||||
|
final AudioDeviceAttributes dev1 =
|
||||||
|
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, "");
|
||||||
|
final AudioDeviceAttributes dev2 =
|
||||||
|
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "C3:P0:beep");
|
||||||
|
final AudioDeviceAttributes dev3 =
|
||||||
|
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "R2:D2:bloop");
|
||||||
|
|
||||||
|
doNothing().when(mMockAudioService).persistSpatialAudioDeviceSettings();
|
||||||
|
|
||||||
|
// test with single device
|
||||||
|
mSpatHelper.addCompatibleAudioDevice(dev1);
|
||||||
|
checkAddSettings();
|
||||||
|
|
||||||
|
// test with 2+ devices so separator character is used in list
|
||||||
|
mSpatHelper.addCompatibleAudioDevice(dev2);
|
||||||
|
Assert.assertTrue(mSpatHelper.isAvailableForDevice(dev2));
|
||||||
|
checkAddSettings();
|
||||||
|
Assert.assertTrue(mSpatHelper.isAvailableForDevice(dev2));
|
||||||
|
mSpatHelper.addCompatibleAudioDevice(dev3);
|
||||||
|
checkAddSettings();
|
||||||
|
|
||||||
|
// test adding a device twice in the list
|
||||||
|
mSpatHelper.addCompatibleAudioDevice(dev1);
|
||||||
|
checkAddSettings();
|
||||||
|
|
||||||
|
// test removing a device
|
||||||
|
mSpatHelper.removeCompatibleAudioDevice(dev2);
|
||||||
|
// spatializer could still be run for dev2 (is available) but spatial audio
|
||||||
|
// is disabled for dev2 by removeCompatibleAudioDevice
|
||||||
|
Assert.assertTrue(mSpatHelper.isAvailableForDevice(dev2));
|
||||||
|
List<AudioDeviceAttributes> compatDevices = mSpatHelper.getCompatibleAudioDevices();
|
||||||
|
Assert.assertFalse(compatDevices.stream().anyMatch(dev -> dev.equalTypeAddress(dev2)));
|
||||||
|
checkAddSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the string representing the current configuration of the devices, then clears it
|
||||||
|
* and restores the configuration. Verify the new string from the restored settings matches
|
||||||
|
* the original one.
|
||||||
|
*/
|
||||||
|
private void checkAddSettings() throws Exception {
|
||||||
|
String settings = mSpatHelper.getSADeviceSettings();
|
||||||
|
Log.i(TAG, "device settings: " + settings);
|
||||||
|
mSpatHelper.clearSADevices();
|
||||||
|
mSpatHelper.setSADeviceSettings(settings);
|
||||||
|
String settingsRestored = mSpatHelper.getSADeviceSettings();
|
||||||
|
Log.i(TAG, "device settingsRestored: " + settingsRestored);
|
||||||
|
Assert.assertEquals(settings, settingsRestored);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user