Commit 4379c9d5 authored by henrika@chromium.org's avatar henrika@chromium.org

Device enumeration on Android now thread safe.

see https://codereview.chromium.org/78033003/ for the original CL...

BUG=324464
TEST=https://simpl.info/getusermedia/sources/ and manual unittests in media.

Review URL: https://codereview.chromium.org/99063002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@238509 0039d316-1c4b-4281-b951-d872f2087c98
parent b59c161d
...@@ -163,6 +163,9 @@ AudioOutputStream* AudioManagerAndroid::MakeLowLatencyOutputStream( ...@@ -163,6 +163,9 @@ AudioOutputStream* AudioManagerAndroid::MakeLowLatencyOutputStream(
AudioInputStream* AudioManagerAndroid::MakeLinearInputStream( AudioInputStream* AudioManagerAndroid::MakeLinearInputStream(
const AudioParameters& params, const std::string& device_id) { const AudioParameters& params, const std::string& device_id) {
// TODO(henrika): add support for device selection if/when any client
// needs it.
DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!";
DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
return new OpenSLESInputStream(this, params); return new OpenSLESInputStream(this, params);
} }
......
...@@ -19,8 +19,8 @@ import android.os.Build; ...@@ -19,8 +19,8 @@ import android.os.Build;
import android.os.Process; import android.os.Process;
import android.util.Log; import android.util.Log;
import java.util.Arrays;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
...@@ -114,6 +114,10 @@ class AudioManagerAndroid { ...@@ -114,6 +114,10 @@ class AudioManagerAndroid {
private Integer mAudioDeviceState = STATE_NO_DEVICE_SELECTED; private Integer mAudioDeviceState = STATE_NO_DEVICE_SELECTED;
// Lock to protect |mAudioDevices| which can be accessed from the main
// thread and the audio manager thread.
private final Object mLock = new Object();
// Contains a list of currently available audio devices. // Contains a list of currently available audio devices.
private boolean[] mAudioDevices = new boolean[DEVICE_COUNT]; private boolean[] mAudioDevices = new boolean[DEVICE_COUNT];
...@@ -141,9 +145,11 @@ class AudioManagerAndroid { ...@@ -141,9 +145,11 @@ class AudioManagerAndroid {
if (mIsInitialized) if (mIsInitialized)
return; return;
synchronized (mLock) {
for (int i = 0; i < DEVICE_COUNT; ++i) { for (int i = 0; i < DEVICE_COUNT; ++i) {
mAudioDevices[i] = false; mAudioDevices[i] = false;
} }
}
// Store microphone mute state and speakerphone state so it can // Store microphone mute state and speakerphone state so it can
// be restored when closing. // be restored when closing.
...@@ -157,10 +163,12 @@ class AudioManagerAndroid { ...@@ -157,10 +163,12 @@ class AudioManagerAndroid {
mAudioDeviceState = STATE_SPEAKERPHONE_ON; mAudioDeviceState = STATE_SPEAKERPHONE_ON;
// Initialize audio device list with things we know is always available. // Initialize audio device list with things we know is always available.
synchronized (mLock) {
if (hasEarpiece()) { if (hasEarpiece()) {
mAudioDevices[DEVICE_EARPIECE] = true; mAudioDevices[DEVICE_EARPIECE] = true;
} }
mAudioDevices[DEVICE_SPEAKERPHONE] = true; mAudioDevices[DEVICE_SPEAKERPHONE] = true;
}
// Register receiver for broadcasted intents related to adding/ // Register receiver for broadcasted intents related to adding/
// removing a wired headset (Intent.ACTION_HEADSET_PLUG). // removing a wired headset (Intent.ACTION_HEADSET_PLUG).
...@@ -213,16 +221,20 @@ class AudioManagerAndroid { ...@@ -213,16 +221,20 @@ class AudioManagerAndroid {
*/ */
@CalledByNative @CalledByNative
public void setDevice(String deviceId) { public void setDevice(String deviceId) {
boolean devices[] = null;
synchronized (mLock) {
devices = mAudioDevices.clone();
}
if (deviceId.isEmpty()) { if (deviceId.isEmpty()) {
logd("setDevice: default"); logd("setDevice: default");
// Use a special selection scheme if the default device is selected. // Use a special selection scheme if the default device is selected.
// The "most unique" device will be selected; Bluetooth first, then // The "most unique" device will be selected; Bluetooth first, then
// wired headset and last the speaker phone. // wired headset and last the speaker phone.
if (mAudioDevices[DEVICE_BLUETOOTH_HEADSET]) { if (devices[DEVICE_BLUETOOTH_HEADSET]) {
// TODO(henrika): possibly need improvements here if we are // TODO(henrika): possibly need improvements here if we are
// in a STATE_BLUETOOTH_TURNING_OFF state. // in a STATE_BLUETOOTH_TURNING_OFF state.
setAudioDevice(DEVICE_BLUETOOTH_HEADSET); setAudioDevice(DEVICE_BLUETOOTH_HEADSET);
} else if (mAudioDevices[DEVICE_WIRED_HEADSET]) { } else if (devices[DEVICE_WIRED_HEADSET]) {
setAudioDevice(DEVICE_WIRED_HEADSET); setAudioDevice(DEVICE_WIRED_HEADSET);
} else { } else {
setAudioDevice(DEVICE_SPEAKERPHONE); setAudioDevice(DEVICE_SPEAKERPHONE);
...@@ -248,8 +260,9 @@ class AudioManagerAndroid { ...@@ -248,8 +260,9 @@ class AudioManagerAndroid {
*/ */
@CalledByNative @CalledByNative
public AudioDeviceName[] getAudioInputDeviceNames() { public AudioDeviceName[] getAudioInputDeviceNames() {
synchronized (mLock) {
List<String> devices = new ArrayList<String>(); List<String> devices = new ArrayList<String>();
AudioDeviceName[] array = new AudioDeviceName[getNumOfAudioDevices()]; AudioDeviceName[] array = new AudioDeviceName[getNumOfAudioDevicesWithLock()];
int i = 0; int i = 0;
for (int id = 0; id < DEVICE_COUNT; ++id ) { for (int id = 0; id < DEVICE_COUNT; ++id ) {
if (mAudioDevices[id]) { if (mAudioDevices[id]) {
...@@ -261,6 +274,7 @@ class AudioManagerAndroid { ...@@ -261,6 +274,7 @@ class AudioManagerAndroid {
logd("getAudioInputDeviceNames: " + devices); logd("getAudioInputDeviceNames: " + devices);
return array; return array;
} }
}
@CalledByNative @CalledByNative
private int getNativeOutputSampleRate() { private int getNativeOutputSampleRate() {
...@@ -363,8 +377,7 @@ class AudioManagerAndroid { ...@@ -363,8 +377,7 @@ class AudioManagerAndroid {
* 'state' value where 0 means unplugged, and 1 means plugged. * 'state' value where 0 means unplugged, and 1 means plugged.
*/ */
private void registerForWiredHeadsetIntentBroadcast() { private void registerForWiredHeadsetIntentBroadcast() {
IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
filter.addAction(Intent.ACTION_HEADSET_PLUG);
/** /**
* Receiver which handles changes in wired headset availablilty. * Receiver which handles changes in wired headset availablilty.
...@@ -378,23 +391,26 @@ class AudioManagerAndroid { ...@@ -378,23 +391,26 @@ class AudioManagerAndroid {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
String action = intent.getAction(); String action = intent.getAction();
if (!action.equals(Intent.ACTION_HEADSET_PLUG)) if (!action.equals(Intent.ACTION_HEADSET_PLUG)) {
return; return;
}
int state = intent.getIntExtra("state", STATE_UNPLUGGED); int state = intent.getIntExtra("state", STATE_UNPLUGGED);
int microphone = intent.getIntExtra("microphone", HAS_NO_MIC); int microphone = intent.getIntExtra("microphone", HAS_NO_MIC);
String name = intent.getStringExtra("name"); String name = intent.getStringExtra("name");
logd("==> onReceive: s=" + state logd("==> onReceive: s=" + state
+ ", m=" + microphone + ", m=" + microphone
+ ", n=" + name + ", n=" + name
+ ", s=" + isInitialStickyBroadcast()); + ", sb=" + isInitialStickyBroadcast());
switch (state) { switch (state) {
case STATE_UNPLUGGED: case STATE_UNPLUGGED:
synchronized (mLock) {
// Wired headset and earpiece are mutually exclusive. // Wired headset and earpiece are mutually exclusive.
mAudioDevices[DEVICE_WIRED_HEADSET] = false; mAudioDevices[DEVICE_WIRED_HEADSET] = false;
if (hasEarpiece()) { if (hasEarpiece()) {
mAudioDevices[DEVICE_EARPIECE] = true; mAudioDevices[DEVICE_EARPIECE] = true;
} }
}
// If wired headset was used before it was unplugged, // If wired headset was used before it was unplugged,
// switch to speaker phone. If it was not in use; just // switch to speaker phone. If it was not in use; just
// log the change. // log the change.
...@@ -405,10 +421,12 @@ class AudioManagerAndroid { ...@@ -405,10 +421,12 @@ class AudioManagerAndroid {
} }
break; break;
case STATE_PLUGGED: case STATE_PLUGGED:
synchronized (mLock) {
// Wired headset and earpiece are mutually exclusive. // Wired headset and earpiece are mutually exclusive.
mAudioDevices[DEVICE_WIRED_HEADSET] = true; mAudioDevices[DEVICE_WIRED_HEADSET] = true;
mAudioDevices[DEVICE_EARPIECE] = false; mAudioDevices[DEVICE_EARPIECE] = false;
setAudioDevice(DEVICE_WIRED_HEADSET); setAudioDevice(DEVICE_WIRED_HEADSET);
}
break; break;
default: default:
loge("Invalid state!"); loge("Invalid state!");
...@@ -472,7 +490,9 @@ class AudioManagerAndroid { ...@@ -472,7 +490,9 @@ class AudioManagerAndroid {
android.bluetooth.BluetoothProfile.STATE_CONNECTED == android.bluetooth.BluetoothProfile.STATE_CONNECTED ==
btAdapter.getProfileConnectionState( btAdapter.getProfileConnectionState(
android.bluetooth.BluetoothProfile.HEADSET)) { android.bluetooth.BluetoothProfile.HEADSET)) {
synchronized (mLock) {
mAudioDevices[DEVICE_BLUETOOTH_HEADSET] = true; mAudioDevices[DEVICE_BLUETOOTH_HEADSET] = true;
}
// TODO(henrika): ensure that we set the active audio // TODO(henrika): ensure that we set the active audio
// device to Bluetooth (not trivial). // device to Bluetooth (not trivial).
setAudioDevice(DEVICE_BLUETOOTH_HEADSET); setAudioDevice(DEVICE_BLUETOOTH_HEADSET);
...@@ -513,7 +533,7 @@ class AudioManagerAndroid { ...@@ -513,7 +533,7 @@ class AudioManagerAndroid {
reportUpdate(); reportUpdate();
} }
private int getNumOfAudioDevices() { private int getNumOfAudioDevicesWithLock() {
int count = 0; int count = 0;
for (int i = 0; i < DEVICE_COUNT; ++i) { for (int i = 0; i < DEVICE_COUNT; ++i) {
if (mAudioDevices[i]) if (mAudioDevices[i])
...@@ -524,11 +544,12 @@ class AudioManagerAndroid { ...@@ -524,11 +544,12 @@ class AudioManagerAndroid {
/** /**
* For now, just log the state change but the idea is that we should * For now, just log the state change but the idea is that we should
* notifies a registered state change listener (if any) that there has * notify a registered state change listener (if any) that there has
* been a change in the state. * been a change in the state.
* TODO(henrika): add support for state change listener. * TODO(henrika): add support for state change listener.
*/ */
private void reportUpdate() { private void reportUpdate() {
synchronized (mLock) {
List<String> devices = new ArrayList<String>(); List<String> devices = new ArrayList<String>();
for (int i = 0; i < DEVICE_COUNT; ++i) { for (int i = 0; i < DEVICE_COUNT; ++i) {
if (mAudioDevices[i]) if (mAudioDevices[i])
...@@ -537,6 +558,7 @@ class AudioManagerAndroid { ...@@ -537,6 +558,7 @@ class AudioManagerAndroid {
logd("reportUpdate: state=" + mAudioDeviceState logd("reportUpdate: state=" + mAudioDeviceState
+ ", devices=" + devices); + ", devices=" + devices);
} }
}
private void logDeviceInfo() { private void logDeviceInfo() {
Log.i(TAG, "Manufacturer:" + Build.MANUFACTURER + Log.i(TAG, "Manufacturer:" + Build.MANUFACTURER +
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment