Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(11)

Unified Diff: trunk/src/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java

Issue 103253005: Revert 240887 "Revert 240885 "Revert 240883 "Refactor audio mana..." (Closed) Base URL: svn://svn.chromium.org/chrome/
Patch Set: Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « trunk/src/media/audio/android/audio_manager_android.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: trunk/src/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
===================================================================
--- trunk/src/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java (revision 240915)
+++ trunk/src/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java (working copy)
@@ -4,6 +4,8 @@
package org.chromium.media;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -18,7 +20,8 @@
import android.media.audiofx.AcousticEchoCanceler;
import android.os.Build;
import android.os.Handler;
-import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Process;
import android.provider.Settings;
import android.util.Log;
@@ -54,7 +57,6 @@
}
// Supported audio device types.
- private static final int DEVICE_DEFAULT = -2;
private static final int DEVICE_INVALID = -1;
private static final int DEVICE_SPEAKERPHONE = 0;
private static final int DEVICE_WIRED_HEADSET = 1;
@@ -81,6 +83,24 @@
DEVICE_BLUETOOTH_HEADSET,
};
+ // The device does not have any audio device.
+ static final int STATE_NO_DEVICE_SELECTED = 0;
+ // The speakerphone is on and an associated microphone is used.
+ static final int STATE_SPEAKERPHONE_ON = 1;
+ // The phone's earpiece is on and an associated microphone is used.
+ static final int STATE_EARPIECE_ON = 2;
+ // A wired headset (with or without a microphone) is plugged in.
+ static final int STATE_WIRED_HEADSET_ON = 3;
+ // The audio stream is being directed to a Bluetooth headset.
+ static final int STATE_BLUETOOTH_ON = 4;
+ // We've requested that the audio stream be directed to Bluetooth, but
+ // have not yet received a response from the framework.
+ static final int STATE_BLUETOOTH_TURNING_ON = 5;
+ // We've requested that the audio stream stop being directed to
+ // Bluetooth, but have not yet received a response from the framework.
+ static final int STATE_BLUETOOTH_TURNING_OFF = 6;
+ // TODO(henrika): document the valid state transitions.
+
// Use 44.1kHz as the default sampling rate.
private static final int DEFAULT_SAMPLING_RATE = 44100;
// Randomly picked up frame size which is close to return value on N4.
@@ -92,18 +112,15 @@
private final Context mContext;
private final long mNativeAudioManagerAndroid;
- private int mSavedAudioMode = AudioManager.MODE_INVALID;
-
+ private boolean mHasBluetoothPermission = false;
private boolean mIsInitialized = false;
private boolean mSavedIsSpeakerphoneOn;
private boolean mSavedIsMicrophoneMute;
- // Id of the requested audio device. Can only be modified by
- // call to setDevice().
- private int mRequestedAudioDevice = DEVICE_INVALID;
+ private Integer mAudioDeviceState = STATE_NO_DEVICE_SELECTED;
- // Lock to protect |mAudioDevices| and |mRequestedAudioDevice| which can
- // be accessed from the main thread and the audio manager thread.
+ // 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.
@@ -111,8 +128,9 @@
private final ContentResolver mContentResolver;
private SettingsObserver mSettingsObserver = null;
- private HandlerThread mSettingsObserverThread = null;
+ private SettingsObserverThread mSettingsObserverThread = null;
private int mCurrentVolume;
+ private final Object mSettingsObserverLock = new Object();
// Broadcast receiver for wired headset intent broadcasts.
private BroadcastReceiver mWiredHeadsetReceiver;
@@ -139,31 +157,56 @@
*/
@CalledByNative
public void init() {
- if (DEBUG) logd("init");
if (mIsInitialized)
return;
- for (int i = 0; i < DEVICE_COUNT; ++i) {
- mAudioDevices[i] = false;
+ synchronized (mLock) {
+ for (int i = 0; i < DEVICE_COUNT; ++i) {
+ mAudioDevices[i] = false;
+ }
}
+ // Store microphone mute state and speakerphone state so it can
+ // be restored when closing.
+ mSavedIsSpeakerphoneOn = mAudioManager.isSpeakerphoneOn();
+ mSavedIsMicrophoneMute = mAudioManager.isMicrophoneMute();
+
+ // Always enable speaker phone by default. This state might be reset
+ // by the wired headset receiver when it gets its initial sticky
+ // intent, if any.
+ setSpeakerphoneOn(true);
+ mAudioDeviceState = STATE_SPEAKERPHONE_ON;
+
// Initialize audio device list with things we know is always available.
- if (hasEarpiece()) {
- mAudioDevices[DEVICE_EARPIECE] = true;
+ synchronized (mLock) {
+ if (hasEarpiece()) {
+ mAudioDevices[DEVICE_EARPIECE] = true;
+ }
+ mAudioDevices[DEVICE_SPEAKERPHONE] = true;
}
- mAudioDevices[DEVICE_SPEAKERPHONE] = true;
// Register receiver for broadcasted intents related to adding/
// removing a wired headset (Intent.ACTION_HEADSET_PLUG).
+ // Also starts routing to the wired headset/headphone if one is
+ // already attached (can be overridden by a Bluetooth headset).
registerForWiredHeadsetIntentBroadcast();
- mSettingsObserverThread = new HandlerThread("SettingsObserver");
- mSettingsObserverThread.start();
- mSettingsObserver = new SettingsObserver(
- new Handler(mSettingsObserverThread.getLooper()));
+ // Start routing to Bluetooth if there's a connected device.
+ // TODO(henrika): the actual routing part is not implemented yet.
+ // All we do currently is to detect if BT headset is attached or not.
+ initBluetooth();
mIsInitialized = true;
- if (DEBUG) reportUpdate();
+
+ mSettingsObserverThread = new SettingsObserverThread();
+ mSettingsObserverThread.start();
+ synchronized (mSettingsObserverLock) {
+ try {
+ mSettingsObserverLock.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "unregisterHeadsetReceiver exception: " + e.getMessage());
+ }
+ }
}
/**
@@ -172,79 +215,33 @@
*/
@CalledByNative
public void close() {
- if (DEBUG) logd("close");
if (!mIsInitialized)
return;
- mSettingsObserverThread.quit();
- try {
- mSettingsObserverThread.join();
- } catch (InterruptedException e) {
- logwtf("HandlerThread.join() exception: " + e.getMessage());
+ if (mSettingsObserverThread != null) {
+ mSettingsObserverThread = null;
}
- mSettingsObserverThread = null;
- mContentResolver.unregisterContentObserver(mSettingsObserver);
- mSettingsObserver = null;
+ if (mSettingsObserver != null) {
+ mContentResolver.unregisterContentObserver(mSettingsObserver);
+ mSettingsObserver = null;
+ }
unregisterForWiredHeadsetIntentBroadcast();
+ // Restore previously stored audio states.
+ setMicrophoneMute(mSavedIsMicrophoneMute);
+ setSpeakerphoneOn(mSavedIsSpeakerphoneOn);
+
mIsInitialized = false;
}
- /**
- * Saves current audio mode and sets audio mode to MODE_IN_COMMUNICATION
- * if input parameter is true. Restores saved audio mode if input parameter
- * is false.
- */
@CalledByNative
- private void setCommunicationAudioModeOn(boolean on) {
- if (DEBUG) logd("setCommunicationAudioModeOn(" + on + ")");
-
- if (on) {
- if (mSavedAudioMode != AudioManager.MODE_INVALID) {
- logwtf("Audio mode has already been set!");
- return;
- }
-
- // Store the current audio mode the first time we try to
- // switch to communication mode.
- try {
- mSavedAudioMode = mAudioManager.getMode();
- } catch (SecurityException e) {
- logwtf("getMode exception: " + e.getMessage());
- logDeviceInfo();
- }
-
- // Store microphone mute state and speakerphone state so it can
- // be restored when closing.
- mSavedIsSpeakerphoneOn = mAudioManager.isSpeakerphoneOn();
- mSavedIsMicrophoneMute = mAudioManager.isMicrophoneMute();
-
- try {
- mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
- } catch (SecurityException e) {
- logwtf("setMode exception: " + e.getMessage());
- logDeviceInfo();
- }
- } else {
- if (mSavedAudioMode == AudioManager.MODE_INVALID) {
- logwtf("Audio mode has not yet been set!");
- return;
- }
-
- // Restore previously stored audio states.
- setMicrophoneMute(mSavedIsMicrophoneMute);
- setSpeakerphoneOn(mSavedIsSpeakerphoneOn);
-
- // Restore the mode that was used before we switched to
- // communication mode.
- try {
- mAudioManager.setMode(mSavedAudioMode);
- } catch (SecurityException e) {
- logwtf("setMode exception: " + e.getMessage());
- logDeviceInfo();
- }
- mSavedAudioMode = AudioManager.MODE_INVALID;
+ public void setMode(int mode) {
+ try {
+ mAudioManager.setMode(mode);
+ } catch (SecurityException e) {
+ Log.e(TAG, "setMode exception: " + e.getMessage());
+ logDeviceInfo();
}
}
@@ -253,36 +250,40 @@
*
* @param deviceId Unique device ID (integer converted to string)
* representing the selected device. This string is empty if the so-called
- * default device is requested.
+ * default device is selected.
*/
@CalledByNative
- private boolean setDevice(String deviceId) {
- if (DEBUG) logd("setDevice: " + deviceId);
- int intDeviceId = deviceId.isEmpty() ?
- DEVICE_DEFAULT : Integer.parseInt(deviceId);
-
- if (intDeviceId == DEVICE_DEFAULT) {
- boolean devices[] = null;
- synchronized (mLock) {
- devices = mAudioDevices.clone();
- mRequestedAudioDevice = DEVICE_DEFAULT;
+ public void setDevice(String deviceId) {
+ boolean devices[] = null;
+ synchronized (mLock) {
+ devices = mAudioDevices.clone();
+ }
+ if (deviceId.isEmpty()) {
+ logd("setDevice: default");
+ // Use a special selection scheme if the default device is selected.
+ // The "most unique" device will be selected; Bluetooth first, then
+ // wired headset and last the speaker phone.
+ if (devices[DEVICE_BLUETOOTH_HEADSET]) {
+ // TODO(henrika): possibly need improvements here if we are
+ // in a STATE_BLUETOOTH_TURNING_OFF state.
+ setAudioDevice(DEVICE_BLUETOOTH_HEADSET);
+ } else if (devices[DEVICE_WIRED_HEADSET]) {
+ setAudioDevice(DEVICE_WIRED_HEADSET);
+ } else {
+ setAudioDevice(DEVICE_SPEAKERPHONE);
}
- int defaultDevice = selectDefaultDevice(devices);
- setAudioDevice(defaultDevice);
- return true;
+ } else {
+ logd("setDevice: " + deviceId);
+ // A non-default device is specified. Verify that it is valid
+ // device, and if so, start using it.
+ List<Integer> validIds = Arrays.asList(VALID_DEVICES);
+ Integer id = Integer.valueOf(deviceId);
+ if (validIds.contains(id)) {
+ setAudioDevice(id.intValue());
+ } else {
+ loge("Invalid device ID!");
+ }
}
-
- // A non-default device is specified. Verify that it is valid
- // device, and if so, start using it.
- List<Integer> validIds = Arrays.asList(VALID_DEVICES);
- if (!validIds.contains(intDeviceId) || !mAudioDevices[intDeviceId]) {
- return false;
- }
- synchronized (mLock) {
- mRequestedAudioDevice = intDeviceId;
- }
- setAudioDevice(intDeviceId);
- return true;
}
/**
@@ -292,23 +293,20 @@
*/
@CalledByNative
public AudioDeviceName[] getAudioInputDeviceNames() {
- boolean devices[] = null;
synchronized (mLock) {
- devices = mAudioDevices.clone();
- }
- List<String> list = new ArrayList<String>();
- AudioDeviceName[] array =
- new AudioDeviceName[getNumOfAudioDevices(devices)];
- int i = 0;
- for (int id = 0; id < DEVICE_COUNT; ++id) {
- if (devices[id]) {
- array[i] = new AudioDeviceName(id, DEVICE_NAMES[id]);
- list.add(DEVICE_NAMES[id]);
- i++;
+ List<String> devices = new ArrayList<String>();
+ AudioDeviceName[] array = new AudioDeviceName[getNumOfAudioDevicesWithLock()];
+ int i = 0;
+ for (int id = 0; id < DEVICE_COUNT; ++id) {
+ if (mAudioDevices[id]) {
+ array[i] = new AudioDeviceName(id, DEVICE_NAMES[id]);
+ devices.add(DEVICE_NAMES[id]);
+ i++;
+ }
}
+ logd("getAudioInputDeviceNames: " + devices);
+ return array;
}
- if (DEBUG) logd("getAudioInputDeviceNames: " + list);
- return array;
}
@CalledByNative
@@ -389,7 +387,7 @@
}
/** Sets the speaker phone mode. */
- private void setSpeakerphoneOn(boolean on) {
+ public void setSpeakerphoneOn(boolean on) {
boolean wasOn = mAudioManager.isSpeakerphoneOn();
if (wasOn == on) {
return;
@@ -398,7 +396,7 @@
}
/** Sets the microphone mute state. */
- private void setMicrophoneMute(boolean on) {
+ public void setMicrophoneMute(boolean on) {
boolean wasMuted = mAudioManager.isMicrophoneMute();
if (wasMuted == on) {
return;
@@ -407,7 +405,7 @@
}
/** Gets the current microphone mute state. */
- private boolean isMicrophoneMute() {
+ public boolean isMicrophoneMute() {
return mAudioManager.isMicrophoneMute();
}
@@ -426,9 +424,7 @@
IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
/**
- * Receiver which handles changes in wired headset availability:
- * updates the list of devices;
- * updates the active device if a device selection has been made.
+ * Receiver which handles changes in wired headset availablilty.
*/
mWiredHeadsetReceiver = new BroadcastReceiver() {
private static final int STATE_UNPLUGGED = 0;
@@ -443,14 +439,13 @@
return;
}
int state = intent.getIntExtra("state", STATE_UNPLUGGED);
- if (DEBUG) {
- int microphone = intent.getIntExtra("microphone", HAS_NO_MIC);
- String name = intent.getStringExtra("name");
- logd("BroadcastReceiver.onReceive: s=" + state
+ int microphone = intent.getIntExtra("microphone", HAS_NO_MIC);
+ String name = intent.getStringExtra("name");
+ logd("==> onReceive: s=" + state
+ ", m=" + microphone
+ ", n=" + name
+ ", sb=" + isInitialStickyBroadcast());
- }
+
switch (state) {
case STATE_UNPLUGGED:
synchronized (mLock) {
@@ -460,30 +455,27 @@
mAudioDevices[DEVICE_EARPIECE] = true;
}
}
+ // If wired headset was used before it was unplugged,
+ // switch to speaker phone. If it was not in use; just
+ // log the change.
+ if (mAudioDeviceState == STATE_WIRED_HEADSET_ON) {
+ setAudioDevice(DEVICE_SPEAKERPHONE);
+ } else {
+ reportUpdate();
+ }
break;
case STATE_PLUGGED:
synchronized (mLock) {
// Wired headset and earpiece are mutually exclusive.
mAudioDevices[DEVICE_WIRED_HEADSET] = true;
mAudioDevices[DEVICE_EARPIECE] = false;
+ setAudioDevice(DEVICE_WIRED_HEADSET);
}
break;
default:
loge("Invalid state!");
break;
}
-
- // Update the existing device selection, but only if a specific
- // device has already been selected explicitly.
- boolean deviceHasBeenRequested = false;
- synchronized (mLock) {
- deviceHasBeenRequested = (mRequestedAudioDevice != DEVICE_INVALID);
- }
- if (deviceHasBeenRequested) {
- updateDeviceActivation();
- } else if (DEBUG) {
- reportUpdate();
- }
}
};
@@ -500,11 +492,62 @@
}
/**
+ * Check if Bluetooth device is connected, register Bluetooth receiver
+ * and start routing to Bluetooth if a device is connected.
+ * TODO(henrika): currently only supports the detecion part at startup.
+ */
+ private void initBluetooth() {
+ // Bail out if we don't have the required permission.
+ mHasBluetoothPermission = mContext.checkPermission(
+ android.Manifest.permission.BLUETOOTH,
+ Process.myPid(),
+ Process.myUid()) == PackageManager.PERMISSION_GRANTED;
+ if (!mHasBluetoothPermission) {
+ loge("BLUETOOTH permission is missing!");
+ return;
+ }
+
+ // To get a BluetoothAdapter representing the local Bluetooth adapter,
+ // when running on JELLY_BEAN_MR1 (4.2) and below, call the static
+ // getDefaultAdapter() method; when running on JELLY_BEAN_MR2 (4.3) and
+ // higher, retrieve it through getSystemService(String) with
+ // BLUETOOTH_SERVICE.
+ // Note: Most methods require the BLUETOOTH permission.
+ BluetoothAdapter btAdapter = null;
+ if (android.os.Build.VERSION.SDK_INT <=
+ android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ // Use static method for Android 4.2 and below to get the
+ // BluetoothAdapter.
+ btAdapter = BluetoothAdapter.getDefaultAdapter();
+ } else {
+ // Use BluetoothManager to get the BluetoothAdapter for
+ // Android 4.3 and above.
+ BluetoothManager btManager = (BluetoothManager) mContext.getSystemService(
+ Context.BLUETOOTH_SERVICE);
+ btAdapter = btManager.getAdapter();
+ }
+
+ if (btAdapter != null &&
+ // android.bluetooth.BluetoothAdapter.getProfileConnectionState
+ // requires BLUETOOTH permission.
+ android.bluetooth.BluetoothProfile.STATE_CONNECTED ==
+ btAdapter.getProfileConnectionState(
+ android.bluetooth.BluetoothProfile.HEADSET)) {
+ synchronized (mLock) {
+ mAudioDevices[DEVICE_BLUETOOTH_HEADSET] = true;
+ }
+ // TODO(henrika): ensure that we set the active audio
+ // device to Bluetooth (not trivial).
+ setAudioDevice(DEVICE_BLUETOOTH_HEADSET);
+ }
+ }
+
+ /**
* Changes selection of the currently active audio device.
*
* @param device Specifies the selected audio device.
*/
- private void setAudioDevice(int device) {
+ public void setAudioDevice(int device) {
switch (device) {
case DEVICE_BLUETOOTH_HEADSET:
// TODO(henrika): add support for turning on an routing to
@@ -513,14 +556,17 @@
break;
case DEVICE_SPEAKERPHONE:
// TODO(henrika): turn off BT if required.
+ mAudioDeviceState = STATE_SPEAKERPHONE_ON;
setSpeakerphoneOn(true);
break;
case DEVICE_WIRED_HEADSET:
// TODO(henrika): turn off BT if required.
+ mAudioDeviceState = STATE_WIRED_HEADSET_ON;
setSpeakerphoneOn(false);
break;
case DEVICE_EARPIECE:
// TODO(henrika): turn off BT if required.
+ mAudioDeviceState = STATE_EARPIECE_ON;
setSpeakerphoneOn(false);
break;
default:
@@ -530,58 +576,11 @@
reportUpdate();
}
- /**
- * Use a special selection scheme if the default device is selected.
- * The "most unique" device will be selected; Wired headset first,
- * then Bluetooth and last the speaker phone.
- */
- private static int selectDefaultDevice(boolean[] devices) {
- if (devices[DEVICE_WIRED_HEADSET]) {
- return DEVICE_WIRED_HEADSET;
- } else if (devices[DEVICE_BLUETOOTH_HEADSET]) {
- // TODO(henrika): possibly need improvements here if we are
- // in a state where Bluetooth is turning off.
- return DEVICE_BLUETOOTH_HEADSET;
- }
- return DEVICE_SPEAKERPHONE;
- }
-
- /**
- * Updates the active device given the current list of devices and
- * information about if a specific device has been selected or if
- * the default device is selected.
- */
- private void updateDeviceActivation() {
- boolean devices[] = null;
- int requested = DEVICE_INVALID;
- synchronized (mLock) {
- requested = mRequestedAudioDevice;
- devices = mAudioDevices.clone();
- }
- if (requested == DEVICE_INVALID) {
- loge("Unable to activate device since no device is selected!");
- return;
- }
-
- // Update default device if it has been selected explicitly, or
- // the selected device has been removed from the list.
- if (requested == DEVICE_DEFAULT || !devices[requested]) {
- // Get default device given current list and activate the device.
- int defaultDevice = selectDefaultDevice(devices);
- setAudioDevice(defaultDevice);
- } else {
- // Activate the selected device since we know that it exists in
- // the list.
- setAudioDevice(requested);
- }
- }
-
- /** Returns number of available devices */
- private static int getNumOfAudioDevices(boolean[] devices) {
+ private int getNumOfAudioDevicesWithLock() {
int count = 0;
for (int i = 0; i < DEVICE_COUNT; ++i) {
- if (devices[i])
- ++count;
+ if (mAudioDevices[i])
+ count++;
}
return count;
}
@@ -599,10 +598,8 @@
if (mAudioDevices[i])
devices.add(DEVICE_NAMES[i]);
}
- if (DEBUG) {
- logd("reportUpdate: requested=" + mRequestedAudioDevice
- + ", devices=" + devices);
- }
+ logd("reportUpdate: state=" + mAudioDeviceState
+ + ", devices=" + devices);
}
}
@@ -622,26 +619,39 @@
Log.e(TAG, msg);
}
- /** What a Terrible Failure: Reports a condition that should never happen */
- private void logwtf(String msg) {
- Log.wtf(TAG, msg);
- }
-
private class SettingsObserver extends ContentObserver {
- SettingsObserver(Handler handler) {
- super(handler);
+ SettingsObserver() {
+ super(new Handler());
mContentResolver.registerContentObserver(Settings.System.CONTENT_URI, true, this);
}
@Override
public void onChange(boolean selfChange) {
- if (DEBUG) logd("SettingsObserver.onChange: " + selfChange);
super.onChange(selfChange);
int volume = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
- if (DEBUG) logd("nativeSetMute: " + (volume == 0));
nativeSetMute(mNativeAudioManagerAndroid, (volume == 0));
}
}
private native void nativeSetMute(long nativeAudioManagerAndroid, boolean muted);
+
+ private class SettingsObserverThread extends Thread {
+ SettingsObserverThread() {
+ super("SettinsObserver");
+ }
+
+ @Override
+ public void run() {
+ // Set this thread up so the handler will work on it.
+ Looper.prepare();
+
+ synchronized (mSettingsObserverLock) {
+ mSettingsObserver = new SettingsObserver();
+ mSettingsObserverLock.notify();
+ }
+
+ // Listen for volume change.
+ Looper.loop();
+ }
+ }
}
« no previous file with comments | « trunk/src/media/audio/android/audio_manager_android.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698