AudioManager: test API for device connection

Add a test API to simulate the connection/disconnection of an
audio device. The simulation of the connection goes as far as
AudioService, but doesn't go into the lower layers of the native
audio policy or the HAL.

Bug: 200218901
Test: atest com.google.android.gts.audioservice.AudioServiceHostTest#testMuteAwaitConnection
Change-Id: I36ae9e07b4c7408a0deb471e9b89263c4db21591
This commit is contained in:
Jean-Michel Trivi
2021-12-03 15:33:46 -08:00
parent 933bf14319
commit 4da775ddb8
6 changed files with 66 additions and 7 deletions

View File

@@ -1458,6 +1458,7 @@ package android.media {
method @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public boolean isPstnCallAudioInterceptable();
method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int requestAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String, int, int);
method public void setRampingRingerEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setTestDeviceConnectionState(@NonNull android.media.AudioDeviceAttributes, boolean);
}
public static final class AudioRecord.MetricsConstants {

View File

@@ -5775,6 +5775,23 @@ public class AudioManager {
}
}
/**
* Indicate wired accessory connection state change.
* @param device {@link AudioDeviceAttributes} of the device to "fake-connect"
* @param connected true for connected, false for disconnected
* {@hide}
*/
@TestApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setTestDeviceConnectionState(@NonNull AudioDeviceAttributes device,
boolean connected) {
try {
getService().setTestDeviceConnectionState(device, connected);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Indicate Bluetooth profile connection state change.
* Configuration changes for A2DP are indicated by having the same <code>newDevice</code> and

View File

@@ -458,4 +458,6 @@ interface IAudioService {
void registerMuteAwaitConnectionDispatcher(in IMuteAwaitConnectionCallback cb,
boolean register);
void setTestDeviceConnectionState(in AudioDeviceAttributes device, boolean connected);
}

View File

@@ -508,6 +508,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
}
/*package*/ void setTestDeviceConnectionState(@NonNull AudioDeviceAttributes device,
@AudioService.ConnectionState int state) {
synchronized (mDeviceStateLock) {
mDeviceInventory.setTestDeviceConnectionState(device, state);
}
}
/*package*/ static final class BleVolumeInfo {
final int mIndex;
final int mMaxIndex;
@@ -1008,7 +1015,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
/*package*/ boolean handleDeviceConnection(boolean connect, int device, String address,
String deviceName) {
synchronized (mDeviceStateLock) {
return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName);
return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName,
false /*for test*/);
}
}

View File

@@ -224,6 +224,7 @@ public class AudioDeviceInventory {
public final String mAddress;
public final String mName;
public final String mCaller;
public boolean mForTest = false;
/*package*/ WiredDeviceConnectionState(int type, @AudioService.ConnectionState int state,
String address, String name, String caller) {
@@ -521,7 +522,7 @@ public class AudioDeviceInventory {
}
if (!handleDeviceConnection(wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED,
wdcs.mType, wdcs.mAddress, wdcs.mName)) {
wdcs.mType, wdcs.mAddress, wdcs.mName, wdcs.mForTest)) {
// change of connection state failed, bailout
mmi.set(MediaMetrics.Property.EARLY_RETURN, "change of connection state failed")
.record();
@@ -681,7 +682,7 @@ public class AudioDeviceInventory {
* @param device the device whose connection state is queried
* @return true if connected
*/
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
// called with AudioDeviceBroker.mDeviceStateLock lock held
public boolean isDeviceConnected(@NonNull AudioDeviceAttributes device) {
final String key = DeviceInfo.makeDeviceListKey(device.getInternalType(),
device.getAddress());
@@ -696,10 +697,12 @@ public class AudioDeviceInventory {
* @param device the device type
* @param address the address of the device
* @param deviceName human-readable name of device
* @param isForTesting if true, not calling AudioSystem for the connection as this is
* just for testing
* @return false if an error was reported by AudioSystem
*/
/*package*/ boolean handleDeviceConnection(boolean connect, int device, String address,
String deviceName) {
String deviceName, boolean isForTesting) {
if (AudioService.DEBUG_DEVICES) {
Slog.i(TAG, "handleDeviceConnection(" + connect + " dev:"
+ Integer.toHexString(device) + " address:" + address
@@ -722,9 +725,14 @@ public class AudioDeviceInventory {
Slog.i(TAG, "deviceInfo:" + di + " is(already)Connected:" + isConnected);
}
if (connect && !isConnected) {
final int res = mAudioSystem.setDeviceConnectionState(device,
AudioSystem.DEVICE_STATE_AVAILABLE, address, deviceName,
AudioSystem.AUDIO_FORMAT_DEFAULT);
final int res;
if (isForTesting) {
res = AudioSystem.AUDIO_STATUS_OK;
} else {
res = mAudioSystem.setDeviceConnectionState(device,
AudioSystem.DEVICE_STATE_AVAILABLE, address, deviceName,
AudioSystem.AUDIO_FORMAT_DEFAULT);
}
if (res != AudioSystem.AUDIO_STATUS_OK) {
final String reason = "not connecting device 0x" + Integer.toHexString(device)
+ " due to command error " + res;
@@ -930,6 +938,15 @@ public class AudioDeviceInventory {
}
}
/*package*/ void setTestDeviceConnectionState(@NonNull AudioDeviceAttributes device,
@AudioService.ConnectionState int state) {
final WiredDeviceConnectionState connection = new WiredDeviceConnectionState(
device.getInternalType(), state, device.getAddress(),
"test device", "com.android.server.audio");
connection.mForTest = true;
onSetWiredDeviceConnectionState(connection);
}
//-------------------------------------------------------------------
// Internal utilities

View File

@@ -6364,6 +6364,20 @@ public class AudioService extends IAudioService.Stub
mDeviceBroker.setWiredDeviceConnectionState(type, state, address, name, caller);
}
/** @see AudioManager#setTestDeviceConnectionState(AudioDeviceAttributes, boolean) */
public void setTestDeviceConnectionState(@NonNull AudioDeviceAttributes device,
boolean connected) {
Objects.requireNonNull(device);
enforceModifyAudioRoutingPermission();
mDeviceBroker.setTestDeviceConnectionState(device,
connected ? CONNECTION_STATE_CONNECTED : CONNECTION_STATE_DISCONNECTED);
// simulate a routing update from native
sendMsg(mAudioHandler,
MSG_ROUTING_UPDATED,
SENDMSG_REPLACE, 0, 0, null,
/*delay*/ 0);
}
/**
* @hide
* The states that can be used with AudioService.setBluetoothHearingAidDeviceConnectionState()