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

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

Issue 110173003: Refactor audio manager for Android to avoid heavy tasks at startup (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Removed BT usage and improved state handling 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
Index: media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
diff --git a/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java b/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
index 0c37c0a9e3a51c92e52e907ba449fcc76cd7019b..ad7a86a495bc97dc40ea274de5c47b5e995d5980 100644
--- a/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
+++ b/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
@@ -40,7 +40,7 @@ class AudioManagerAndroid {
private static final String TAG = "AudioManagerAndroid";
// Set to true to enable debug logs. Always check in as false.
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
henrika (OOO until Aug 14) 2013/12/10 16:23:18 will be restored to false
/** Simple container for device information. */
private static class AudioDeviceName {
@@ -115,10 +115,12 @@ class AudioManagerAndroid {
private final Context mContext;
private final long mNativeAudioManagerAndroid;
- private boolean mHasBluetoothPermission = false;
+ private int mSavedAudioMode = AudioManager.MODE_INVALID;
+
private boolean mIsInitialized = false;
private boolean mSavedIsSpeakerphoneOn;
private boolean mSavedIsMicrophoneMute;
+ private boolean mDefaultDeviceIsSelected = false;
private Integer mAudioDeviceState = STATE_NO_DEVICE_SELECTED;
@@ -160,6 +162,7 @@ class AudioManagerAndroid {
*/
@CalledByNative
public void init() {
+ if (DEBUG) logd("init");
if (mIsInitialized)
return;
@@ -169,17 +172,6 @@ class AudioManagerAndroid {
}
}
- // 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.
synchronized (mLock) {
if (hasEarpiece()) {
@@ -190,15 +182,8 @@ class AudioManagerAndroid {
// 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();
- // 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;
tommi (sloooow) - chröme 2013/12/10 21:19:11 nit: set last?
henrika (OOO until Aug 14) 2013/12/11 13:16:38 Done.
mSettingsObserverThread = new SettingsObserverThread();
@@ -207,7 +192,7 @@ class AudioManagerAndroid {
try {
mSettingsObserverLock.wait();
tommi (sloooow) - chröme 2013/12/10 21:19:11 I'm not sure we need this locking+waiting actually
henrika (OOO until Aug 14) 2013/12/11 13:16:38 Wei wrote these things with assistance from review
} catch (InterruptedException e) {
- Log.e(TAG, "unregisterHeadsetReceiver exception: " + e.getMessage());
+ loge("unregisterHeadsetReceiver exception: " + e.getMessage());
tommi (sloooow) - chröme 2013/12/10 21:19:11 nit: log message seems misleading
henrika (OOO until Aug 14) 2013/12/11 13:16:38 Changed. Code from Wei.
}
}
}
@@ -218,6 +203,7 @@ class AudioManagerAndroid {
*/
@CalledByNative
public void close() {
+ if (DEBUG) logd("close");
if (!mIsInitialized)
return;
@@ -231,20 +217,53 @@ class AudioManagerAndroid {
unregisterForWiredHeadsetIntentBroadcast();
- // Restore previously stored audio states.
- setMicrophoneMute(mSavedIsMicrophoneMute);
- setSpeakerphoneOn(mSavedIsSpeakerphoneOn);
-
mIsInitialized = false;
}
+ /**
+ * TODO(henrika): add comments...
Jói 2013/12/11 10:52:04 Before landing this patch?
henrika (OOO until Aug 14) 2013/12/12 10:58:00 Done.
+ */
@CalledByNative
- public void setMode(int mode) {
- try {
- mAudioManager.setMode(mode);
- } catch (SecurityException e) {
- Log.e(TAG, "setMode exception: " + e.getMessage());
- logDeviceInfo();
+ public void setCommunicationAudioModeOn(boolean on) {
+ if (DEBUG) logd("setCommunicationAudioModeOn(" + on + ")");
+
+ if (on) {
+ if (mSavedAudioMode == AudioManager.MODE_INVALID) {
tommi (sloooow) - chröme 2013/12/10 21:19:11 What about making it a programmer error if mSavedA
henrika (OOO until Aug 14) 2013/12/12 10:58:00 Will improve.
+ // Store the current audio mode the first time we try to
+ // switch to communication mode.
+ try {
+ mSavedAudioMode = mAudioManager.getMode();
+ } catch (SecurityException e) {
+ loge("getMode exception: " + e.getMessage());
tommi (sloooow) - chröme 2013/12/10 21:19:11 does this happen in practice?
henrika (OOO until Aug 14) 2013/12/11 13:16:38 It was added in the original version and I simply
+ 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) {
+ loge("setMode exception: " + e.getMessage());
+ logDeviceInfo();
+ }
+ } else {
+ if (mSavedAudioMode != AudioManager.MODE_INVALID) {
tommi (sloooow) - chröme 2013/12/10 21:19:11 assert that mSavedAudioMode != AudioManager.MODE_I
henrika (OOO until Aug 14) 2013/12/11 13:16:38 Done. Assuming my "assert" is OK.
+ // 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) {
+ loge("setMode exception: " + e.getMessage());
+ logDeviceInfo();
+ }
+ }
tommi (sloooow) - chröme 2013/12/10 21:19:11 set mSavedAudioMode to AudioManager.MODE_INVALID a
henrika (OOO until Aug 14) 2013/12/11 13:16:38 Done.
}
}
@@ -256,37 +275,41 @@ class AudioManagerAndroid {
* default device is selected.
*/
@CalledByNative
- public void setDevice(String deviceId) {
+ boolean setDevice(String deviceId) {
boolean devices[] = null;
synchronized (mLock) {
devices = mAudioDevices.clone();
}
if (deviceId.isEmpty()) {
- logd("setDevice: default");
+ if (DEBUG) 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]) {
+ // The "most unique" device will be selected; Wired headset first,
+ // then Bluetooth and last the speaker phone.
+ if (devices[DEVICE_WIRED_HEADSET]) {
+ setAudioDevice(DEVICE_WIRED_HEADSET);
+ } else 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);
}
- } else {
- logd("setDevice: " + deviceId);
+ mDefaultDeviceIsSelected = true;
+ return true;
+ } else if (isNumeric(deviceId)) {
+ if (DEBUG) 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)) {
+ if (validIds.contains(id) && mAudioDevices[id.intValue()]) {
setAudioDevice(id.intValue());
- } else {
- loge("Invalid device ID!");
+ mDefaultDeviceIsSelected = false;
+ return true;
}
}
+ loge("Invalid device ID: " + deviceId);
+ return false;
}
/**
@@ -307,7 +330,7 @@ class AudioManagerAndroid {
i++;
}
}
- logd("getAudioInputDeviceNames: " + devices);
+ if (DEBUG) logd("getAudioInputDeviceNames: " + devices);
return array;
}
}
@@ -433,11 +456,12 @@ class AudioManagerAndroid {
int state = intent.getIntExtra("state", STATE_UNPLUGGED);
int microphone = intent.getIntExtra("microphone", HAS_NO_MIC);
String name = intent.getStringExtra("name");
- logd("==> onReceive: s=" + state
+ if (DEBUG) {
+ logd("==> onReceive: s=" + state
+ ", m=" + microphone
+ ", n=" + name
+ ", sb=" + isInitialStickyBroadcast());
-
+ }
switch (state) {
case STATE_UNPLUGGED:
synchronized (mLock) {
@@ -447,27 +471,25 @@ class AudioManagerAndroid {
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.
+ // TODO(henrika): add lock?
tommi (sloooow) - chröme 2013/12/10 21:19:11 on which thread are we here? If we're on a differe
henrika (OOO until Aug 14) 2013/12/11 13:16:38 Will take a second round and consider these issues
+ if (mAudioDeviceState != STATE_NO_DEVICE_SELECTED) {
+ updateDeviceSelection();
+ }
}
};
@@ -484,58 +506,6 @@ class AudioManagerAndroid {
}
/**
- * 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.
@@ -578,6 +548,48 @@ class AudioManagerAndroid {
return count;
}
+ /** TODO(henrika): add comments... */
Jói 2013/12/11 10:52:04 Before landing this patch?
henrika (OOO until Aug 14) 2013/12/11 13:16:38 Done.
+ private void updateDeviceSelection() {
+ final String DEFAULT_DEVICE_ID = "";
+ if (DEBUG) logd("updateDeviceSelection");
+
+ // Something has been changed in the device state. A device could
+ // have been added or removed. Take actions given the current state.
+ if (mDefaultDeviceIsSelected) {
+ // Deafault-device mode => shift to new default device.
tommi (sloooow) - chröme 2013/12/10 21:19:11 Default
henrika (OOO until Aug 14) 2013/12/11 13:16:38 Done.
+ setDevice(DEFAULT_DEVICE_ID);
+ } else {
+ // Non-default mode => shift to default device mode if the
+ // currently selected device is no longer available.
+ // TODO(henrika): is there anything else we can do here?
tommi (sloooow) - chröme 2013/12/10 21:19:11 Could we keep track of what device was previously
henrika (OOO until Aug 14) 2013/12/11 13:16:38 I think I simply have to develop this scheme as we
+ if (!selectedDeviceIsInList()) {
+ if (DEBUG) logd("Falling back to default device");
+ setDevice(DEFAULT_DEVICE_ID);
+ } else {
+ if (DEBUG) logd("No action required!");
+ }
+ }
+ }
+
+ private static boolean isNumeric(String str) {
tommi (sloooow) - chröme 2013/12/10 21:19:11 is this necessary? Would Integer.parseInt() give
Jói 2013/12/11 10:52:04 That would throw an exception if it's not an integ
tommi (sloooow) - chröme 2013/12/11 12:34:25 Yes, I think it would be an exceptional case and s
henrika (OOO until Aug 14) 2013/12/11 13:16:38 I tried to avoid exception. Will not happen often.
tommi (sloooow) - chröme 2013/12/11 17:25:05 yes I think that's OK.
henrika (OOO until Aug 14) 2013/12/12 10:58:00 Done.
+ for (char c : str.toCharArray())
tommi (sloooow) - chröme 2013/12/10 21:19:11 for () { }
henrika (OOO until Aug 14) 2013/12/11 13:16:38 Done.
+ {
+ if (!Character.isDigit(c)) return false;
tommi (sloooow) - chröme 2013/12/10 21:19:11 return on a separate line
henrika (OOO until Aug 14) 2013/12/11 13:16:38 Done.
+ }
+ return true;
+ }
+
+ private boolean selectedDeviceIsInList() {
+ return (mAudioDeviceState == STATE_SPEAKERPHONE_ON &&
tommi (sloooow) - chröme 2013/12/10 21:19:11 crazy idea: It seems that all the STATE_FOO_ON co
henrika (OOO until Aug 14) 2013/12/11 13:16:38 The idea crossed my mind as well but I had also ad
+ mAudioDevices[DEVICE_SPEAKERPHONE]) ||
+ (mAudioDeviceState == STATE_WIRED_HEADSET_ON &&
+ mAudioDevices[DEVICE_WIRED_HEADSET]) ||
+ (mAudioDeviceState == STATE_EARPIECE_ON &&
+ mAudioDevices[DEVICE_EARPIECE]) ||
+ (mAudioDeviceState == STATE_BLUETOOTH_ON &&
+ mAudioDevices[DEVICE_BLUETOOTH_HEADSET]);
+ }
+
/**
* For now, just log the state change but the idea is that we should
* notify a registered state change listener (if any) that there has
@@ -591,8 +603,10 @@ class AudioManagerAndroid {
if (mAudioDevices[i])
devices.add(DEVICE_NAMES[i]);
}
- logd("reportUpdate: state=" + mAudioDeviceState
- + ", devices=" + devices);
+ if (DEBUG) {
+ logd("reportUpdate: state=" + mAudioDeviceState
+ + ", devices=" + devices);
+ }
}
}

Powered by Google App Engine
This is Rietveld 408576698