Chromium Code Reviews| 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); |
| + } |
| } |
| } |