Loading services/core/java/com/android/server/audio/AudioDeviceInventory.java +22 −17 Original line number Diff line number Diff line Loading @@ -77,6 +77,9 @@ public class AudioDeviceInventory { // List of preferred devices for strategies private final ArrayMap<Integer, AudioDeviceAddress> mPreferredDevices = new ArrayMap<>(); // the wrapper for AudioSystem static methods, allows us to spy AudioSystem private final @NonNull AudioSystemAdapter mAudioSystem; private @NonNull AudioDeviceBroker mDeviceBroker; // Monitoring of audio routes. Protected by mAudioRoutes. Loading @@ -86,12 +89,14 @@ public class AudioDeviceInventory { /*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) { mDeviceBroker = broker; mAudioSystem = AudioSystemAdapter.getDefaultAdapter(); } //----------------------------------------------------------- /** for mocking only */ /*package*/ AudioDeviceInventory() { /** for mocking only, allows to inject AudioSystem adapter */ /*package*/ AudioDeviceInventory(@NonNull AudioSystemAdapter audioSystem) { mDeviceBroker = null; mAudioSystem = audioSystem; } /*package*/ void setDeviceBroker(@NonNull AudioDeviceBroker broker) { Loading Loading @@ -185,7 +190,7 @@ public class AudioDeviceInventory { synchronized (mDevicesLock) { //TODO iterate on mApmConnectedDevices instead once it handles all device types for (DeviceInfo di : mConnectedDevices.values()) { AudioSystem.setDeviceConnectionState( mAudioSystem.setDeviceConnectionState( di.mDeviceType, AudioSystem.DEVICE_STATE_AVAILABLE, di.mDeviceAddress, Loading @@ -195,7 +200,7 @@ public class AudioDeviceInventory { } synchronized (mPreferredDevices) { mPreferredDevices.forEach((strategy, device) -> { AudioSystem.setPreferredDeviceForStrategy(strategy, device); }); mAudioSystem.setPreferredDeviceForStrategy(strategy, device); }); } } Loading Loading @@ -355,7 +360,7 @@ public class AudioDeviceInventory { mConnectedDevices.replace(key, di); } } final int res = AudioSystem.handleDeviceConfigChange( final int res = mAudioSystem.handleDeviceConfigChange( AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, BtHelper.getName(btDevice), a2dpCodec); Loading Loading @@ -477,7 +482,7 @@ public class AudioDeviceInventory { /*package*/ int setPreferredDeviceForStrategySync(int strategy, @NonNull AudioDeviceAddress device) { final long identity = Binder.clearCallingIdentity(); final int status = AudioSystem.setPreferredDeviceForStrategy(strategy, device); final int status = mAudioSystem.setPreferredDeviceForStrategy(strategy, device); Binder.restoreCallingIdentity(identity); if (status == AudioSystem.SUCCESS) { Loading @@ -488,7 +493,7 @@ public class AudioDeviceInventory { /*package*/ int removePreferredDeviceForStrategySync(int strategy) { final long identity = Binder.clearCallingIdentity(); final int status = AudioSystem.removePreferredDeviceForStrategy(strategy); final int status = mAudioSystem.removePreferredDeviceForStrategy(strategy); Binder.restoreCallingIdentity(identity); if (status == AudioSystem.SUCCESS) { Loading Loading @@ -523,7 +528,7 @@ public class AudioDeviceInventory { Slog.i(TAG, "deviceInfo:" + di + " is(already)Connected:" + isConnected); } if (connect && !isConnected) { final int res = AudioSystem.setDeviceConnectionState(device, final int res = mAudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE, address, deviceName, AudioSystem.AUDIO_FORMAT_DEFAULT); if (res != AudioSystem.AUDIO_STATUS_OK) { Loading @@ -536,7 +541,7 @@ public class AudioDeviceInventory { mDeviceBroker.postAccessoryPlugMediaUnmute(device); return true; } else if (!connect && isConnected) { AudioSystem.setDeviceConnectionState(device, mAudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_UNAVAILABLE, address, deviceName, AudioSystem.AUDIO_FORMAT_DEFAULT); // always remove even if disconnection failed Loading Loading @@ -713,7 +718,7 @@ public class AudioDeviceInventory { mDeviceBroker.setBluetoothA2dpOnInt(true, 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 final int res = AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, final int res = mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_AVAILABLE, address, name, a2dpCodec); if (res != AudioSystem.AUDIO_STATUS_OK) { Loading @@ -728,7 +733,7 @@ public class AudioDeviceInventory { } // Reset A2DP suspend state each time a new sink is connected AudioSystem.setParameters("A2dpSuspended=false"); mAudioSystem.setParameters("A2dpSuspended=false"); final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name, address, a2dpCodec); Loading Loading @@ -761,7 +766,7 @@ public class AudioDeviceInventory { // device to remove was visible by APM, update APM mDeviceBroker.setAvrcpAbsoluteVolumeSupported(false); final int res = AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, final int res = mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", a2dpCodec); if (res != AudioSystem.AUDIO_STATUS_OK) { Loading @@ -783,7 +788,7 @@ public class AudioDeviceInventory { private void makeA2dpDeviceUnavailableLater(String address, int delayMs) { // prevent any activity on the A2DP audio output to avoid unwanted // reconnection of the sink. AudioSystem.setParameters("A2dpSuspended=true"); mAudioSystem.setParameters("A2dpSuspended=true"); // retrieve DeviceInfo before removing device final String deviceKey = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address); Loading @@ -799,7 +804,7 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeA2dpSrcAvailable(String address) { AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_AVAILABLE, address, "", AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.put( Loading @@ -810,7 +815,7 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeA2dpSrcUnavailable(String address) { AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.remove( Loading @@ -824,7 +829,7 @@ public class AudioDeviceInventory { AudioSystem.DEVICE_OUT_HEARING_AID); mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, streamType); AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID, mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID, AudioSystem.DEVICE_STATE_AVAILABLE, address, name, AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.put( Loading @@ -839,7 +844,7 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeHearingAidDeviceUnavailable(String address) { AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID, mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID, AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.remove( Loading services/core/java/com/android/server/audio/AudioSystemAdapter.java 0 → 100644 +155 −0 Original line number Diff line number Diff line /* * Copyright 2019 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 android.annotation.NonNull; import android.media.AudioDeviceAddress; import android.media.AudioSystem; import android.util.Log; /** * Provides an adapter to access functionality of the android.media.AudioSystem class for device * related functionality. * Use the "real" AudioSystem through the default adapter. * Use the "always ok" adapter to avoid dealing with the APM behaviors during a test. */ public class AudioSystemAdapter { /** * Create a wrapper around the {@link AudioSystem} static methods, all functions are directly * forwarded to the AudioSystem class. * @return an adapter around AudioSystem */ static final @NonNull AudioSystemAdapter getDefaultAdapter() { return new AudioSystemAdapter(); } /** * Create an adapter for AudioSystem that always succeeds, and does nothing. * @return a no-op AudioSystem adapter */ static final @NonNull AudioSystemAdapter getAlwaysOkAdapter() { return new AudioSystemOkAdapter(); } /** * Same as {@link AudioSystem#setDeviceConnectionState(int, int, String, String, int)} * @param device * @param state * @param deviceAddress * @param deviceName * @param codecFormat * @return */ public int setDeviceConnectionState(int device, int state, String deviceAddress, String deviceName, int codecFormat) { return AudioSystem.setDeviceConnectionState(device, state, deviceAddress, deviceName, codecFormat); } /** * Same as {@link AudioSystem#getDeviceConnectionState(int, String)} * @param device * @param deviceAddress * @return */ public int getDeviceConnectionState(int device, String deviceAddress) { return AudioSystem.getDeviceConnectionState(device, deviceAddress); } /** * Same as {@link AudioSystem#handleDeviceConfigChange(int, String, String, int)} * @param device * @param deviceAddress * @param deviceName * @param codecFormat * @return */ public int handleDeviceConfigChange(int device, String deviceAddress, String deviceName, int codecFormat) { return AudioSystem.handleDeviceConfigChange(device, deviceAddress, deviceName, codecFormat); } /** * Same as {@link AudioSystem#setPreferredDeviceForStrategy(int, AudioDeviceAddress)} * @param strategy * @param device * @return */ public int setPreferredDeviceForStrategy(int strategy, @NonNull AudioDeviceAddress device) { return AudioSystem.setPreferredDeviceForStrategy(strategy, device); } /** * Same as {@link AudioSystem#removePreferredDeviceForStrategy(int)} * @param strategy * @return */ public int removePreferredDeviceForStrategy(int strategy) { return AudioSystem.removePreferredDeviceForStrategy(strategy); } /** * Same as {@link AudioSystem#setParameters(String)} * @param keyValuePairs * @return */ public int setParameters(String keyValuePairs) { return AudioSystem.setParameters(keyValuePairs); } //-------------------------------------------------------------------- protected static class AudioSystemOkAdapter extends AudioSystemAdapter { private static final String TAG = "ASA"; @Override public int setDeviceConnectionState(int device, int state, String deviceAddress, String deviceName, int codecFormat) { Log.i(TAG, String.format("setDeviceConnectionState(0x%s, %s, %s, 0x%s", Integer.toHexString(device), state, deviceAddress, deviceName, Integer.toHexString(codecFormat))); return AudioSystem.AUDIO_STATUS_OK; } @Override public int getDeviceConnectionState(int device, String deviceAddress) { return AudioSystem.AUDIO_STATUS_OK; } @Override public int handleDeviceConfigChange(int device, String deviceAddress, String deviceName, int codecFormat) { return AudioSystem.AUDIO_STATUS_OK; } @Override public int setPreferredDeviceForStrategy(int strategy, @NonNull AudioDeviceAddress device) { return AudioSystem.AUDIO_STATUS_OK; } @Override public int removePreferredDeviceForStrategy(int strategy) { return AudioSystem.AUDIO_STATUS_OK; } @Override public int setParameters(String keyValuePairs) { return AudioSystem.AUDIO_STATUS_OK; } } } services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java +15 −4 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ public class AudioDeviceBrokerTest { @Mock private AudioService mMockAudioService; @Spy private AudioDeviceInventory mSpyDevInventory; @Spy private AudioSystemAdapter mSpyAudioSystem; private BluetoothDevice mFakeBtDevice; Loading @@ -65,7 +66,8 @@ public class AudioDeviceBrokerTest { mContext = InstrumentationRegistry.getTargetContext(); mMockAudioService = mock(AudioService.class); mSpyDevInventory = spy(new AudioDeviceInventory()); mSpyAudioSystem = spy(AudioSystemAdapter.getAlwaysOkAdapter()); mSpyDevInventory = spy(new AudioDeviceInventory(mSpyAudioSystem)); mAudioDeviceBroker = new AudioDeviceBroker(mContext, mMockAudioService, mSpyDevInventory); mSpyDevInventory.setDeviceBroker(mAudioDeviceBroker); Loading @@ -81,8 +83,9 @@ public class AudioDeviceBrokerTest { public void testSetUpAndTearDown() { } /** * Verify call to postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent() for connection * calls into AudioDeviceInventory with the right params * postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent() for connection: * - verify it calls into AudioDeviceInventory with the right params * - verify it calls into AudioSystem and stays connected (no 2nd call to disconnect) * @throws Exception */ @Test Loading @@ -92,7 +95,7 @@ public class AudioDeviceBrokerTest { mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1); Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); Thread.sleep(2 * MAX_MESSAGE_HANDLING_DELAY_MS); verify(mSpyDevInventory, times(1)).setBluetoothA2dpDeviceConnectionState( any(BluetoothDevice.class), ArgumentMatchers.eq(BluetoothProfile.STATE_CONNECTED) /*state*/, Loading @@ -100,6 +103,14 @@ public class AudioDeviceBrokerTest { ArgumentMatchers.eq(true) /*suppressNoisyIntent*/, anyInt() /*musicDevice*/, ArgumentMatchers.eq(1) /*a2dpVolume*/ ); final String expectedName = mFakeBtDevice.getName() == null ? "" : mFakeBtDevice.getName(); verify(mSpyAudioSystem, times(1)).setDeviceConnectionState( ArgumentMatchers.eq(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP), ArgumentMatchers.eq(AudioSystem.DEVICE_STATE_AVAILABLE), ArgumentMatchers.eq(mFakeBtDevice.getAddress()), ArgumentMatchers.eq(expectedName), anyInt() /*codec*/); } /** Loading Loading
services/core/java/com/android/server/audio/AudioDeviceInventory.java +22 −17 Original line number Diff line number Diff line Loading @@ -77,6 +77,9 @@ public class AudioDeviceInventory { // List of preferred devices for strategies private final ArrayMap<Integer, AudioDeviceAddress> mPreferredDevices = new ArrayMap<>(); // the wrapper for AudioSystem static methods, allows us to spy AudioSystem private final @NonNull AudioSystemAdapter mAudioSystem; private @NonNull AudioDeviceBroker mDeviceBroker; // Monitoring of audio routes. Protected by mAudioRoutes. Loading @@ -86,12 +89,14 @@ public class AudioDeviceInventory { /*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) { mDeviceBroker = broker; mAudioSystem = AudioSystemAdapter.getDefaultAdapter(); } //----------------------------------------------------------- /** for mocking only */ /*package*/ AudioDeviceInventory() { /** for mocking only, allows to inject AudioSystem adapter */ /*package*/ AudioDeviceInventory(@NonNull AudioSystemAdapter audioSystem) { mDeviceBroker = null; mAudioSystem = audioSystem; } /*package*/ void setDeviceBroker(@NonNull AudioDeviceBroker broker) { Loading Loading @@ -185,7 +190,7 @@ public class AudioDeviceInventory { synchronized (mDevicesLock) { //TODO iterate on mApmConnectedDevices instead once it handles all device types for (DeviceInfo di : mConnectedDevices.values()) { AudioSystem.setDeviceConnectionState( mAudioSystem.setDeviceConnectionState( di.mDeviceType, AudioSystem.DEVICE_STATE_AVAILABLE, di.mDeviceAddress, Loading @@ -195,7 +200,7 @@ public class AudioDeviceInventory { } synchronized (mPreferredDevices) { mPreferredDevices.forEach((strategy, device) -> { AudioSystem.setPreferredDeviceForStrategy(strategy, device); }); mAudioSystem.setPreferredDeviceForStrategy(strategy, device); }); } } Loading Loading @@ -355,7 +360,7 @@ public class AudioDeviceInventory { mConnectedDevices.replace(key, di); } } final int res = AudioSystem.handleDeviceConfigChange( final int res = mAudioSystem.handleDeviceConfigChange( AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, BtHelper.getName(btDevice), a2dpCodec); Loading Loading @@ -477,7 +482,7 @@ public class AudioDeviceInventory { /*package*/ int setPreferredDeviceForStrategySync(int strategy, @NonNull AudioDeviceAddress device) { final long identity = Binder.clearCallingIdentity(); final int status = AudioSystem.setPreferredDeviceForStrategy(strategy, device); final int status = mAudioSystem.setPreferredDeviceForStrategy(strategy, device); Binder.restoreCallingIdentity(identity); if (status == AudioSystem.SUCCESS) { Loading @@ -488,7 +493,7 @@ public class AudioDeviceInventory { /*package*/ int removePreferredDeviceForStrategySync(int strategy) { final long identity = Binder.clearCallingIdentity(); final int status = AudioSystem.removePreferredDeviceForStrategy(strategy); final int status = mAudioSystem.removePreferredDeviceForStrategy(strategy); Binder.restoreCallingIdentity(identity); if (status == AudioSystem.SUCCESS) { Loading Loading @@ -523,7 +528,7 @@ public class AudioDeviceInventory { Slog.i(TAG, "deviceInfo:" + di + " is(already)Connected:" + isConnected); } if (connect && !isConnected) { final int res = AudioSystem.setDeviceConnectionState(device, final int res = mAudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE, address, deviceName, AudioSystem.AUDIO_FORMAT_DEFAULT); if (res != AudioSystem.AUDIO_STATUS_OK) { Loading @@ -536,7 +541,7 @@ public class AudioDeviceInventory { mDeviceBroker.postAccessoryPlugMediaUnmute(device); return true; } else if (!connect && isConnected) { AudioSystem.setDeviceConnectionState(device, mAudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_UNAVAILABLE, address, deviceName, AudioSystem.AUDIO_FORMAT_DEFAULT); // always remove even if disconnection failed Loading Loading @@ -713,7 +718,7 @@ public class AudioDeviceInventory { mDeviceBroker.setBluetoothA2dpOnInt(true, 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 final int res = AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, final int res = mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_AVAILABLE, address, name, a2dpCodec); if (res != AudioSystem.AUDIO_STATUS_OK) { Loading @@ -728,7 +733,7 @@ public class AudioDeviceInventory { } // Reset A2DP suspend state each time a new sink is connected AudioSystem.setParameters("A2dpSuspended=false"); mAudioSystem.setParameters("A2dpSuspended=false"); final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name, address, a2dpCodec); Loading Loading @@ -761,7 +766,7 @@ public class AudioDeviceInventory { // device to remove was visible by APM, update APM mDeviceBroker.setAvrcpAbsoluteVolumeSupported(false); final int res = AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, final int res = mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", a2dpCodec); if (res != AudioSystem.AUDIO_STATUS_OK) { Loading @@ -783,7 +788,7 @@ public class AudioDeviceInventory { private void makeA2dpDeviceUnavailableLater(String address, int delayMs) { // prevent any activity on the A2DP audio output to avoid unwanted // reconnection of the sink. AudioSystem.setParameters("A2dpSuspended=true"); mAudioSystem.setParameters("A2dpSuspended=true"); // retrieve DeviceInfo before removing device final String deviceKey = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address); Loading @@ -799,7 +804,7 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeA2dpSrcAvailable(String address) { AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_AVAILABLE, address, "", AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.put( Loading @@ -810,7 +815,7 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeA2dpSrcUnavailable(String address) { AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.remove( Loading @@ -824,7 +829,7 @@ public class AudioDeviceInventory { AudioSystem.DEVICE_OUT_HEARING_AID); mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, streamType); AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID, mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID, AudioSystem.DEVICE_STATE_AVAILABLE, address, name, AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.put( Loading @@ -839,7 +844,7 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeHearingAidDeviceUnavailable(String address) { AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID, mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID, AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", AudioSystem.AUDIO_FORMAT_DEFAULT); mConnectedDevices.remove( Loading
services/core/java/com/android/server/audio/AudioSystemAdapter.java 0 → 100644 +155 −0 Original line number Diff line number Diff line /* * Copyright 2019 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 android.annotation.NonNull; import android.media.AudioDeviceAddress; import android.media.AudioSystem; import android.util.Log; /** * Provides an adapter to access functionality of the android.media.AudioSystem class for device * related functionality. * Use the "real" AudioSystem through the default adapter. * Use the "always ok" adapter to avoid dealing with the APM behaviors during a test. */ public class AudioSystemAdapter { /** * Create a wrapper around the {@link AudioSystem} static methods, all functions are directly * forwarded to the AudioSystem class. * @return an adapter around AudioSystem */ static final @NonNull AudioSystemAdapter getDefaultAdapter() { return new AudioSystemAdapter(); } /** * Create an adapter for AudioSystem that always succeeds, and does nothing. * @return a no-op AudioSystem adapter */ static final @NonNull AudioSystemAdapter getAlwaysOkAdapter() { return new AudioSystemOkAdapter(); } /** * Same as {@link AudioSystem#setDeviceConnectionState(int, int, String, String, int)} * @param device * @param state * @param deviceAddress * @param deviceName * @param codecFormat * @return */ public int setDeviceConnectionState(int device, int state, String deviceAddress, String deviceName, int codecFormat) { return AudioSystem.setDeviceConnectionState(device, state, deviceAddress, deviceName, codecFormat); } /** * Same as {@link AudioSystem#getDeviceConnectionState(int, String)} * @param device * @param deviceAddress * @return */ public int getDeviceConnectionState(int device, String deviceAddress) { return AudioSystem.getDeviceConnectionState(device, deviceAddress); } /** * Same as {@link AudioSystem#handleDeviceConfigChange(int, String, String, int)} * @param device * @param deviceAddress * @param deviceName * @param codecFormat * @return */ public int handleDeviceConfigChange(int device, String deviceAddress, String deviceName, int codecFormat) { return AudioSystem.handleDeviceConfigChange(device, deviceAddress, deviceName, codecFormat); } /** * Same as {@link AudioSystem#setPreferredDeviceForStrategy(int, AudioDeviceAddress)} * @param strategy * @param device * @return */ public int setPreferredDeviceForStrategy(int strategy, @NonNull AudioDeviceAddress device) { return AudioSystem.setPreferredDeviceForStrategy(strategy, device); } /** * Same as {@link AudioSystem#removePreferredDeviceForStrategy(int)} * @param strategy * @return */ public int removePreferredDeviceForStrategy(int strategy) { return AudioSystem.removePreferredDeviceForStrategy(strategy); } /** * Same as {@link AudioSystem#setParameters(String)} * @param keyValuePairs * @return */ public int setParameters(String keyValuePairs) { return AudioSystem.setParameters(keyValuePairs); } //-------------------------------------------------------------------- protected static class AudioSystemOkAdapter extends AudioSystemAdapter { private static final String TAG = "ASA"; @Override public int setDeviceConnectionState(int device, int state, String deviceAddress, String deviceName, int codecFormat) { Log.i(TAG, String.format("setDeviceConnectionState(0x%s, %s, %s, 0x%s", Integer.toHexString(device), state, deviceAddress, deviceName, Integer.toHexString(codecFormat))); return AudioSystem.AUDIO_STATUS_OK; } @Override public int getDeviceConnectionState(int device, String deviceAddress) { return AudioSystem.AUDIO_STATUS_OK; } @Override public int handleDeviceConfigChange(int device, String deviceAddress, String deviceName, int codecFormat) { return AudioSystem.AUDIO_STATUS_OK; } @Override public int setPreferredDeviceForStrategy(int strategy, @NonNull AudioDeviceAddress device) { return AudioSystem.AUDIO_STATUS_OK; } @Override public int removePreferredDeviceForStrategy(int strategy) { return AudioSystem.AUDIO_STATUS_OK; } @Override public int setParameters(String keyValuePairs) { return AudioSystem.AUDIO_STATUS_OK; } } }
services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java +15 −4 Original line number Diff line number Diff line Loading @@ -57,6 +57,7 @@ public class AudioDeviceBrokerTest { @Mock private AudioService mMockAudioService; @Spy private AudioDeviceInventory mSpyDevInventory; @Spy private AudioSystemAdapter mSpyAudioSystem; private BluetoothDevice mFakeBtDevice; Loading @@ -65,7 +66,8 @@ public class AudioDeviceBrokerTest { mContext = InstrumentationRegistry.getTargetContext(); mMockAudioService = mock(AudioService.class); mSpyDevInventory = spy(new AudioDeviceInventory()); mSpyAudioSystem = spy(AudioSystemAdapter.getAlwaysOkAdapter()); mSpyDevInventory = spy(new AudioDeviceInventory(mSpyAudioSystem)); mAudioDeviceBroker = new AudioDeviceBroker(mContext, mMockAudioService, mSpyDevInventory); mSpyDevInventory.setDeviceBroker(mAudioDeviceBroker); Loading @@ -81,8 +83,9 @@ public class AudioDeviceBrokerTest { public void testSetUpAndTearDown() { } /** * Verify call to postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent() for connection * calls into AudioDeviceInventory with the right params * postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent() for connection: * - verify it calls into AudioDeviceInventory with the right params * - verify it calls into AudioSystem and stays connected (no 2nd call to disconnect) * @throws Exception */ @Test Loading @@ -92,7 +95,7 @@ public class AudioDeviceBrokerTest { mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1); Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); Thread.sleep(2 * MAX_MESSAGE_HANDLING_DELAY_MS); verify(mSpyDevInventory, times(1)).setBluetoothA2dpDeviceConnectionState( any(BluetoothDevice.class), ArgumentMatchers.eq(BluetoothProfile.STATE_CONNECTED) /*state*/, Loading @@ -100,6 +103,14 @@ public class AudioDeviceBrokerTest { ArgumentMatchers.eq(true) /*suppressNoisyIntent*/, anyInt() /*musicDevice*/, ArgumentMatchers.eq(1) /*a2dpVolume*/ ); final String expectedName = mFakeBtDevice.getName() == null ? "" : mFakeBtDevice.getName(); verify(mSpyAudioSystem, times(1)).setDeviceConnectionState( ArgumentMatchers.eq(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP), ArgumentMatchers.eq(AudioSystem.DEVICE_STATE_AVAILABLE), ArgumentMatchers.eq(mFakeBtDevice.getAddress()), ArgumentMatchers.eq(expectedName), anyInt() /*codec*/); } /** Loading