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 5fbadca16f81314696c9e504c2430b159c19e654..237fb1541fc3e5e32568a463b7a36a9baf74eb8e 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,40 @@ class AudioManagerAndroid { |
| // NOTE: always check in as false. |
| private static final boolean DEBUG = false; |
| + /** |
| + * NonThreadSafe is a helper class used to help verify that methods of a |
| + * class are called from the same thread. |
| + * Inspired by class in package com.google.android.apps.chrome.utilities. |
| + * Is only utilized when DEBUG is set to true. |
| + */ |
| + private static class NonThreadSafe { |
| + private Long mThreadId = 0L; |
|
bulach
2014/02/18 13:32:07
nit: make it final, and assign directly in the con
henrika (OOO until Aug 14)
2014/02/18 14:19:15
Thanks. I had added a detach method first as well
|
| + |
| + public NonThreadSafe() { |
| + if (DEBUG) { |
| + ensureThreadIdAssigned(); |
| + } |
| + } |
| + |
| + /** |
| + * Checks if the method is called on the valid thread. |
| + * Assigns the current thread if no thread was assigned. |
| + */ |
| + public boolean calledOnValidThread() { |
| + if (DEBUG) { |
| + ensureThreadIdAssigned(); |
| + return mThreadId.equals(Thread.currentThread().getId()); |
| + } |
| + return true; |
| + } |
| + |
| + private void ensureThreadIdAssigned() { |
| + if (DEBUG) { |
| + if (mThreadId == 0L) mThreadId = Thread.currentThread().getId(); |
| + } |
| + } |
| + } |
| + |
| private static boolean runningOnJellyBeanOrHigher() { |
| return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; |
| } |
| @@ -135,6 +169,11 @@ class AudioManagerAndroid { |
| // call to setDevice(). |
| private int mRequestedAudioDevice = DEVICE_INVALID; |
| + // This class should be created, initialized and closed on the audio thread |
| + // in the audio manager. We use |mNonThreadSafe| to ensure that this is |
| + // the case. Only active when |DEBUG| is set to true. |
| + private final NonThreadSafe mNonThreadSafe = new NonThreadSafe(); |
| + |
| // Lock to protect |mAudioDevices| and |mRequestedAudioDevice| which can |
| // be accessed from the main thread and the audio manager thread. |
| private final Object mLock = new Object(); |
| @@ -177,21 +216,19 @@ class AudioManagerAndroid { |
| * Saves the initial speakerphone and microphone state. |
| * Populates the list of available audio devices and registers receivers |
| * for broadcast intents related to wired headset and Bluetooth devices. |
| + * TODO(henrika): investigate if it would be possible to move code in |
| + * init() to the constructor and code in close() to finalize(). |
|
bulach
2014/02/18 13:32:07
is finalize() a new method you'll introduce or a j
henrika (OOO until Aug 14)
2014/02/18 14:19:15
Thanks for pointing that out; I have been warned a
|
| */ |
| @CalledByNative |
| private void init() { |
| + checkIfCalledOnValidThread(); |
| if (DEBUG) logd("init"); |
| if (mIsInitialized) |
| return; |
| - for (int i = 0; i < DEVICE_COUNT; ++i) { |
| - mAudioDevices[i] = false; |
| - } |
| - |
| // Initialize audio device list with things we know is always available. |
| - if (hasEarpiece()) { |
| - mAudioDevices[DEVICE_EARPIECE] = true; |
| - } |
| + mAudioDevices[DEVICE_EARPIECE] = hasEarpiece(); |
| + mAudioDevices[DEVICE_WIRED_HEADSET] = hasWiredHeadset(); |
| mAudioDevices[DEVICE_SPEAKERPHONE] = true; |
| // Register receivers for broadcast intents related to Bluetooth device |
| @@ -218,6 +255,7 @@ class AudioManagerAndroid { |
| } |
| mIsInitialized = true; |
| + |
| if (DEBUG) reportUpdate(); |
| } |
| @@ -227,6 +265,7 @@ class AudioManagerAndroid { |
| */ |
| @CalledByNative |
| private void close() { |
| + checkIfCalledOnValidThread(); |
| if (DEBUG) logd("close"); |
| if (!mIsInitialized) |
| return; |
| @@ -354,6 +393,7 @@ class AudioManagerAndroid { |
| */ |
| @CalledByNative |
| private AudioDeviceName[] getAudioInputDeviceNames() { |
| + if (DEBUG) logd("getAudioInputDeviceNames"); |
| if (!mIsInitialized) |
| return null; |
| boolean devices[] = null; |
| @@ -442,7 +482,7 @@ class AudioManagerAndroid { |
| } |
| @CalledByNative |
| - public static boolean shouldUseAcousticEchoCanceler() { |
| + private static boolean shouldUseAcousticEchoCanceler() { |
| // AcousticEchoCanceler was added in API level 16 (Jelly Bean). |
| if (!runningOnJellyBeanOrHigher()) { |
| return false; |
| @@ -450,14 +490,14 @@ class AudioManagerAndroid { |
| // Next is a list of device models which have been vetted for good |
| // quality platform echo cancellation. |
| - if (!Build.MODEL.equals("SM-T310R") && // Galaxy Tab 3 7.0 |
| - !Build.MODEL.equals("GT-I9300") && // Galaxy S3 |
| + if (!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")) { |
| + !Build.MODEL.equals("Nexus 7") && |
| + !Build.MODEL.equals("SM-N9005") && // Galaxy Note 3 |
| + !Build.MODEL.equals("SM-T310R")) { // Galaxy Tab 3 7.0 |
| return false; |
| } |
| @@ -467,6 +507,16 @@ class AudioManagerAndroid { |
| } |
| /** |
| + * Helper method for debugging purposes. Logs message if method is not |
| + * called on same thread as this object was created on. |
| + */ |
| + private void checkIfCalledOnValidThread() { |
| + if (DEBUG && !mNonThreadSafe.calledOnValidThread()) { |
| + Log.wtf(TAG,"Method is not called on valid thread!"); |
|
bulach
2014/02/18 13:32:07
nit: space after ,
henrika (OOO until Aug 14)
2014/02/18 14:19:15
impressive ;-)
|
| + } |
| + } |
| + |
| + /** |
| * Register for BT intents if we have the BLUETOOTH permission. |
| * Also extends the list of available devices with a BT device if one exists. |
| */ |
| @@ -483,9 +533,7 @@ class AudioManagerAndroid { |
| if (!mHasBluetoothPermission) { |
| return; |
| } |
| - if (hasBluetoothHeadset()) { |
| - mAudioDevices[DEVICE_BLUETOOTH_HEADSET] = true; |
| - } |
| + mAudioDevices[DEVICE_BLUETOOTH_HEADSET] = hasBluetoothHeadset(); |
| // Register receivers for broadcast intents related to changes in |
| // Bluetooth headset availability and usage of the SCO channel. |
| @@ -525,12 +573,24 @@ class AudioManagerAndroid { |
| return mAudioManager.isMicrophoneMute(); |
| } |
| - /** Gets the current earpice state. */ |
| + /** Gets the current earpiece state. */ |
| private boolean hasEarpiece() { |
| return mContext.getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_TELEPHONY); |
| } |
| + /** |
| + * Checks whether a wired headset is connected or not. |
| + * This is not a valid indication that audio playback is actually over |
| + * the wired headset as audio routing depends on other conditions. We |
| + * only use it as an early indicator (during initialization) of an attached |
| + * wired headset. |
| + */ |
| + @Deprecated |
| + private boolean hasWiredHeadset() { |
| + return mAudioManager.isWiredHeadsetOn(); |
| + } |
| + |
| /** Checks if the process has BLUETOOTH permission or not. */ |
| private boolean hasBluetoothPermission() { |
| boolean hasBluetooth = mContext.checkPermission( |