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 a60b69018cf97e598308ce0ab34b0eb989ba217f..18f22d574323c207d2c3415048c12d253624e1a2 100644 |
| --- a/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java |
| +++ b/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java |
| @@ -38,7 +38,41 @@ class AudioManagerAndroid { |
| // Set to true to enable debug logs. Avoid in production builds. |
| // NOTE: always check in as false. |
| - private static final boolean DEBUG = false; |
| + private static final boolean DEBUG = true; |
|
tommi (sloooow) - chröme
2014/02/04 14:30:12
remember to revert.
henrika (OOO until Aug 14)
2014/02/05 09:45:57
I will. Remember ;-)
|
| + |
| + /** |
| + * 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; |
| + |
| + 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(); |
| + } |
| + } |
| + } |
| /** Simple container for device information. */ |
| private static class AudioDeviceName { |
| @@ -123,6 +157,12 @@ class AudioManagerAndroid { |
| // call to setDevice(). |
| private int mRequestedAudioDevice = DEVICE_INVALID; |
| + // This class should be created, initialized and closed on the main |
| + // Java thread. In addition, BroadcastReceiver.onReceive should also |
| + // happen on this thread. 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(); |
| @@ -155,6 +195,7 @@ class AudioManagerAndroid { |
| } |
| private AudioManagerAndroid(Context context, long nativeAudioManagerAndroid) { |
| + logd("@AudioManagerAndroid: " + Thread.currentThread()); |
| mContext = context; |
| mNativeAudioManagerAndroid = nativeAudioManagerAndroid; |
| mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); |
| @@ -165,21 +206,23 @@ 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(). |
| */ |
| @CalledByNative |
| private void init() { |
| + logd("@init: " + Thread.currentThread()); |
| + if (!mNonThreadSafe.calledOnValidThread()) { |
| + logwtf("init is not called on valid thread!"); |
| + return; |
| + } |
| 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 |
| @@ -196,6 +239,7 @@ class AudioManagerAndroid { |
| new Handler(mSettingsObserverThread.getLooper())); |
| mIsInitialized = true; |
| + |
| if (DEBUG) reportUpdate(); |
| } |
| @@ -205,10 +249,18 @@ class AudioManagerAndroid { |
| */ |
| @CalledByNative |
| private void close() { |
| + logd("@close: " + Thread.currentThread()); |
| + if (!mNonThreadSafe.calledOnValidThread()) { |
|
tommi (sloooow) - chröme
2014/02/04 14:30:12
Could this be consolidated into a method?
private
henrika (OOO until Aug 14)
2014/02/05 09:45:57
Done.
|
| + logwtf("close is not called on valid thread!"); |
| + return; |
| + } |
| if (DEBUG) logd("close"); |
| if (!mIsInitialized) |
| return; |
| + unregisterForWiredHeadsetIntentBroadcast(); |
| + unregisterBluetoothIntentsIfNeeded(); |
| + |
| mSettingsObserverThread.quit(); |
| try { |
| mSettingsObserverThread.join(); |
| @@ -219,9 +271,6 @@ class AudioManagerAndroid { |
| mContentResolver.unregisterContentObserver(mSettingsObserver); |
| mSettingsObserver = null; |
| - unregisterForWiredHeadsetIntentBroadcast(); |
| - unregisterBluetoothIntentsIfNeeded(); |
| - |
| mIsInitialized = false; |
| } |
| @@ -232,6 +281,7 @@ class AudioManagerAndroid { |
| */ |
| @CalledByNative |
| private void setCommunicationAudioModeOn(boolean on) { |
| + logd("@setCommunicationAudioModeOn: " + Thread.currentThread()); |
| if (DEBUG) logd("setCommunicationAudioModeOn(" + on + ")"); |
| if (on) { |
| @@ -291,7 +341,9 @@ class AudioManagerAndroid { |
| */ |
| @CalledByNative |
| private boolean setDevice(String deviceId) { |
| + logd("@setDevice: " + Thread.currentThread()); |
| if (DEBUG) logd("setDevice: " + deviceId); |
| + |
| int intDeviceId = deviceId.isEmpty() ? |
| DEVICE_DEFAULT : Integer.parseInt(deviceId); |
| @@ -326,6 +378,8 @@ class AudioManagerAndroid { |
| */ |
| @CalledByNative |
| private AudioDeviceName[] getAudioInputDeviceNames() { |
| + logd("@getAudioInputDeviceNames: " + Thread.currentThread()); |
| + if (DEBUG) logd("getAudioInputDeviceNames"); |
|
tommi (sloooow) - chröme
2014/02/04 14:30:12
double logging? in general I think we can do with
|
| boolean devices[] = null; |
| synchronized (mLock) { |
| devices = mAudioDevices.clone(); |
| @@ -347,6 +401,7 @@ class AudioManagerAndroid { |
| @CalledByNative |
| private int getNativeOutputSampleRate() { |
| + logd("@getNativeOutputSampleRate: " + Thread.currentThread()); |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { |
| String sampleRateString = mAudioManager.getProperty( |
| AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); |
| @@ -365,6 +420,7 @@ class AudioManagerAndroid { |
| */ |
| @CalledByNative |
| private static int getMinInputFrameSize(int sampleRate, int channels) { |
| + logd("@getMinInputFrameSize: " + Thread.currentThread()); |
| int channelConfig; |
| if (channels == 1) { |
| channelConfig = AudioFormat.CHANNEL_IN_MONO; |
| @@ -385,6 +441,7 @@ class AudioManagerAndroid { |
| */ |
| @CalledByNative |
| private static int getMinOutputFrameSize(int sampleRate, int channels) { |
| + logd("@getMinOutputFrameSize: " + Thread.currentThread()); |
| int channelConfig; |
| if (channels == 1) { |
| channelConfig = AudioFormat.CHANNEL_OUT_MONO; |
| @@ -399,12 +456,14 @@ class AudioManagerAndroid { |
| @CalledByNative |
| private boolean isAudioLowLatencySupported() { |
| + logd("@isAudioLowLatencySupported: " + Thread.currentThread()); |
| return mContext.getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_AUDIO_LOW_LATENCY); |
| } |
| @CalledByNative |
| private int getAudioLowLatencyOutputFrameSize() { |
| + logd("@getAudioLowLatencyOutputFrameSize: " + Thread.currentThread()); |
| String framesPerBuffer = |
| mAudioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER); |
| return (framesPerBuffer == null ? |
| @@ -412,7 +471,8 @@ class AudioManagerAndroid { |
| } |
| @CalledByNative |
| - public static boolean shouldUseAcousticEchoCanceler() { |
| + private static boolean shouldUseAcousticEchoCanceler() { |
| + logd("@shouldUseAcousticEchoCanceler: " + Thread.currentThread()); |
| // AcousticEchoCanceler was added in API level 16 (Jelly Bean). |
| // Next is a list of device models which have been vetted for good |
| // quality platform echo cancellation. |
| @@ -445,9 +505,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. |
| @@ -487,12 +545,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( |
| @@ -559,6 +629,11 @@ class AudioManagerAndroid { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| + logd("@onReceive: " + Thread.currentThread()); |
| + if (!mNonThreadSafe.calledOnValidThread()) { |
| + logwtf("onReceive is not called on valid thread!"); |
| + return; |
| + } |
| int state = intent.getIntExtra("state", STATE_UNPLUGGED); |
| if (DEBUG) { |
| int microphone = intent.getIntExtra("microphone", HAS_NO_MIC); |
| @@ -627,6 +702,10 @@ class AudioManagerAndroid { |
| mBluetoothHeadsetReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| + if (!mNonThreadSafe.calledOnValidThread()) { |
| + logwtf("onReceive is not called on valid thread!"); |
| + return; |
| + } |
| // A change in connection state of the Headset profile has |
| // been detected, e.g. BT headset has been connected or |
| // disconnected. This broadcast is *not* sticky. |