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 a60b69018cf97e598308ce0ab34b0eb989ba217f..87f8ca11cbe44e731ce7650f4dceb2dcb68dfceb 100644 |
--- a/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java |
+++ b/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java |
@@ -40,6 +40,14 @@ class AudioManagerAndroid { |
// NOTE: always check in as false. |
private static final boolean DEBUG = false; |
+ private static boolean runningOnJellyBeanOrHigher() { |
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; |
+ } |
+ |
+ private static boolean runningOnJellyBeanMR1OrHigher() { |
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1; |
+ } |
+ |
/** Simple container for device information. */ |
private static class AudioDeviceName { |
private final int mId; |
@@ -190,10 +198,20 @@ class AudioManagerAndroid { |
// removing a wired headset (Intent.ACTION_HEADSET_PLUG). |
registerForWiredHeadsetIntentBroadcast(); |
- mSettingsObserverThread = new HandlerThread("SettingsObserver"); |
- mSettingsObserverThread.start(); |
- mSettingsObserver = new SettingsObserver( |
- new Handler(mSettingsObserverThread.getLooper())); |
+ // Start observer for volume changes. |
+ // TODO(henrika): try-catch parts below are added as a test to see if |
+ // it avoids the crash in init() reported in http://crbug.com/336600. |
+ // Should be removed if possible when we understand the reason better. |
+ try { |
+ mSettingsObserverThread = new HandlerThread("SettingsObserver"); |
+ mSettingsObserverThread.start(); |
+ mSettingsObserver = new SettingsObserver( |
+ new Handler(mSettingsObserverThread.getLooper())); |
+ } catch (Exception e) { |
+ // It is fine to rely on code below here to detect failure by |
+ // observing mSettingsObserver==null. |
+ Log.wtf(TAG, "SettingsObserver exception: ", e); |
+ } |
mIsInitialized = true; |
Ami GONE FROM CHROMIUM
2014/02/10 18:29:47
You still aren't early-returning from the catch cl
henrika (OOO until Aug 14)
2014/02/10 19:33:40
Yes, that is a design decision. We see this issue
Ami GONE FROM CHROMIUM
2014/02/10 19:42:30
I assumed that one a WTF exception happened we wou
henrika (OOO until Aug 14)
2014/02/10 20:02:19
A valid comment. Yes, I could bail out early but t
|
if (DEBUG) reportUpdate(); |
@@ -209,15 +227,19 @@ class AudioManagerAndroid { |
if (!mIsInitialized) |
return; |
- mSettingsObserverThread.quit(); |
- try { |
- mSettingsObserverThread.join(); |
- } catch (InterruptedException e) { |
- logwtf("HandlerThread.join() exception: " + e.getMessage()); |
+ if (mSettingsObserverThread != null) { |
+ mSettingsObserverThread.quit(); |
+ try { |
+ mSettingsObserverThread.join(); |
+ } catch (Exception e) { |
+ Log.wtf(TAG, "HandlerThread.join() exception: ", e); |
+ } |
+ mSettingsObserverThread = null; |
+ } |
+ if (mContentResolver != null) { |
+ mContentResolver.unregisterContentObserver(mSettingsObserver); |
+ mSettingsObserver = null; |
} |
- mSettingsObserverThread = null; |
- mContentResolver.unregisterContentObserver(mSettingsObserver); |
- mSettingsObserver = null; |
unregisterForWiredHeadsetIntentBroadcast(); |
unregisterBluetoothIntentsIfNeeded(); |
@@ -236,7 +258,7 @@ class AudioManagerAndroid { |
if (on) { |
if (mSavedAudioMode != AudioManager.MODE_INVALID) { |
- logwtf("Audio mode has already been set!"); |
+ Log.wtf(TAG, "Audio mode has already been set!"); |
return; |
} |
@@ -245,7 +267,7 @@ class AudioManagerAndroid { |
try { |
mSavedAudioMode = mAudioManager.getMode(); |
} catch (SecurityException e) { |
- logwtf("getMode exception: " + e.getMessage()); |
+ Log.wtf(TAG, "getMode exception: ", e); |
logDeviceInfo(); |
} |
@@ -257,12 +279,12 @@ class AudioManagerAndroid { |
try { |
mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); |
} catch (SecurityException e) { |
- logwtf("setMode exception: " + e.getMessage()); |
+ Log.wtf(TAG, "setMode exception: ", e); |
logDeviceInfo(); |
} |
} else { |
if (mSavedAudioMode == AudioManager.MODE_INVALID) { |
- logwtf("Audio mode has not yet been set!"); |
+ Log.wtf(TAG, "Audio mode has not yet been set!"); |
return; |
} |
@@ -275,7 +297,7 @@ class AudioManagerAndroid { |
try { |
mAudioManager.setMode(mSavedAudioMode); |
} catch (SecurityException e) { |
- logwtf("setMode exception: " + e.getMessage()); |
+ Log.wtf(TAG, "setMode exception: ", e); |
logDeviceInfo(); |
} |
mSavedAudioMode = AudioManager.MODE_INVALID; |
@@ -292,6 +314,8 @@ class AudioManagerAndroid { |
@CalledByNative |
private boolean setDevice(String deviceId) { |
if (DEBUG) logd("setDevice: " + deviceId); |
+ if (!mIsInitialized) |
+ return false; |
int intDeviceId = deviceId.isEmpty() ? |
DEVICE_DEFAULT : Integer.parseInt(deviceId); |
@@ -326,6 +350,8 @@ class AudioManagerAndroid { |
*/ |
@CalledByNative |
private AudioDeviceName[] getAudioInputDeviceNames() { |
+ if (!mIsInitialized) |
+ return null; |
boolean devices[] = null; |
synchronized (mLock) { |
devices = mAudioDevices.clone(); |
@@ -347,7 +373,7 @@ class AudioManagerAndroid { |
@CalledByNative |
private int getNativeOutputSampleRate() { |
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { |
+ if (runningOnJellyBeanMR1OrHigher()) { |
String sampleRateString = mAudioManager.getProperty( |
AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); |
return (sampleRateString == null ? |
@@ -414,18 +440,26 @@ class AudioManagerAndroid { |
@CalledByNative |
public static boolean shouldUseAcousticEchoCanceler() { |
// AcousticEchoCanceler was added in API level 16 (Jelly Bean). |
+ if (!runningOnJellyBeanOrHigher()) { |
+ return false; |
+ } |
+ |
// Next is a list of device models which have been vetted for good |
// quality platform echo cancellation. |
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && |
- AcousticEchoCanceler.isAvailable() && |
- (Build.MODEL.equals("SM-T310R") || // Galaxy Tab 3 7.0 |
- Build.MODEL.equals("GT-I9300") || // Galaxy S3 |
- Build.MODEL.equals("GT-I9500") || // Galaxy S4 |
- Build.MODEL.equals("GT-N7105") || // Galaxy Note 2 |
- Build.MODEL.equals("Nexus 4") || |
- Build.MODEL.equals("Nexus 5") || |
- Build.MODEL.equals("Nexus 7") || |
- Build.MODEL.equals("SM-N9005")); // Galaxy Note 3 |
+ if (!Build.MODEL.equals("SM-T310R") && // Galaxy Tab 3 7.0 |
+ !Build.MODEL.equals("GT-I9300") && // Galaxy S3 |
+ !Build.MODEL.equals("GT-I9500") && // Galaxy S4 |
+ !Build.MODEL.equals("GT-N7105") && // Galaxy Note 2 |
+ !Build.MODEL.equals("SM-N9005") && // Galaxy Note 3 |
+ !Build.MODEL.equals("Nexus 4") && |
+ !Build.MODEL.equals("Nexus 5") && |
+ !Build.MODEL.equals("Nexus 7")) { |
+ return false; |
+ } |
+ |
+ // As a final check, verify that the device supports acoustic echo |
+ // cancellation. |
+ return AcousticEchoCanceler.isAvailable(); |
} |
/** |
@@ -512,7 +546,7 @@ class AudioManagerAndroid { |
*/ |
private boolean hasBluetoothHeadset() { |
if (!mHasBluetoothPermission) { |
- logwtf("hasBluetoothHeadset() requires BLUETOOTH permission!"); |
+ Log.wtf(TAG, "hasBluetoothHeadset() requires BLUETOOTH permission!"); |
return false; |
} |
@@ -522,24 +556,45 @@ class AudioManagerAndroid { |
// higher, retrieve it through getSystemService(String) with |
// BLUETOOTH_SERVICE. |
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 { |
+ if (runningOnJellyBeanMR1OrHigher()) { |
// Use BluetoothManager to get the BluetoothAdapter for |
// Android 4.3 and above. |
- BluetoothManager btManager = |
- (BluetoothManager)mContext.getSystemService( |
- Context.BLUETOOTH_SERVICE); |
- btAdapter = btManager.getAdapter(); |
+ try { |
+ BluetoothManager btManager = |
+ (BluetoothManager)mContext.getSystemService( |
+ Context.BLUETOOTH_SERVICE); |
+ btAdapter = btManager.getAdapter(); |
+ } catch (Exception e) { |
+ Log.wtf(TAG, "BluetoothManager.getAdapter exception", e); |
+ return false; |
+ } |
+ } else { |
+ // Use static method for Android 4.2 and below to get the |
+ // BluetoothAdapter. |
+ try { |
+ btAdapter = BluetoothAdapter.getDefaultAdapter(); |
+ } catch (Exception e) { |
+ Log.wtf(TAG, "BluetoothAdapter.getDefaultAdapter exception", e); |
+ return false; |
+ } |
+ } |
+ |
+ int profileConnectionState; |
+ try { |
+ profileConnectionState = btAdapter.getProfileConnectionState( |
+ android.bluetooth.BluetoothProfile.HEADSET); |
+ } catch (Exception e) { |
+ Log.wtf(TAG, "BluetoothAdapter.getProfileConnectionState exception", e); |
+ profileConnectionState = |
+ android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; |
} |
- return (btAdapter != null && |
- android.bluetooth.BluetoothProfile.STATE_CONNECTED == |
- btAdapter.getProfileConnectionState( |
- android.bluetooth.BluetoothProfile.HEADSET)); |
+ // Ensure that Bluetooth is enabled and that a device which supports the |
+ // headset and handsfree profile is connected. |
+ // TODO(henrika): it is possible that btAdapter.isEnabled() is |
+ // redundant. It might be sufficient to only check the profile state. |
+ return btAdapter.isEnabled() && profileConnectionState == |
+ android.bluetooth.BluetoothProfile.STATE_CONNECTED; |
} |
/** |
@@ -911,11 +966,6 @@ class AudioManagerAndroid { |
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); |