summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build/android/findbugs_filter/findbugs_known_bugs.txt2
-rw-r--r--media/audio/android/audio_manager_android.cc28
-rw-r--r--media/audio/android/audio_manager_android.h16
-rw-r--r--media/audio/android/opensles_output.cc9
-rw-r--r--media/audio/android/opensles_output.h10
-rw-r--r--media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java78
6 files changed, 138 insertions, 5 deletions
diff --git a/build/android/findbugs_filter/findbugs_known_bugs.txt b/build/android/findbugs_filter/findbugs_known_bugs.txt
index 09e403f..5ac2ae0 100644
--- a/build/android/findbugs_filter/findbugs_known_bugs.txt
+++ b/build/android/findbugs_filter/findbugs_known_bugs.txt
@@ -26,6 +26,8 @@ M D SF: Switch statement found in org.chromium.chrome.browser.database.SQLiteCur
M D SF: Switch statement found in org.chromium.content.browser.third_party.GestureDetector.onTouchEvent(MotionEvent) where default case is missing At GestureDetector.java
M M UG: org.chromium.content.browser.JavaBridgeReturnValuesTest$TestObject.getBooleanValue() is unsynchronized, org.chromium.content.browser.JavaBridgeReturnValuesTest$TestObject.setBooleanValue(boolean) is synchronized At JavaBridgeReturnValuesTest.java
M M UG: org.chromium.content.browser.JavaBridgeReturnValuesTest$TestObject.getStringValue() is unsynchronized, org.chromium.content.browser.JavaBridgeReturnValuesTest$TestObject.setStringValue(String) is synchronized At JavaBridgeReturnValuesTest.java
+M M UW: Unconditional wait in org.chromium.media.AudioManagerAndroid.init() At AudioManagerAndroid.java
+M M Wa: Wait not in loop in org.chromium.media.AudioManagerAndroid.init() At AudioManagerAndroid.java
M V EI2: new org.chromium.chrome.browser.FindMatchRectsDetails(int, RectF[], RectF) may expose internal representation by storing an externally mutable object into FindMatchRectsDetails.rects At FindMatchRectsDetails.java
M V EI2: org.chromium.chrome.browser.ChromeBrowserProvider$BookmarkNode.setFavicon(byte[]) may expose internal representation by storing an externally mutable object into ChromeBrowserProvider$BookmarkNode.mFavicon At ChromeBrowserProvider.java
M V EI2: org.chromium.chrome.browser.ChromeBrowserProvider$BookmarkNode.setThumbnail(byte[]) may expose internal representation by storing an externally mutable object into ChromeBrowserProvider$BookmarkNode.mThumbnail At ChromeBrowserProvider.java
diff --git a/media/audio/android/audio_manager_android.cc b/media/audio/android/audio_manager_android.cc
index 2e16a1f..b588768 100644
--- a/media/audio/android/audio_manager_android.cc
+++ b/media/audio/android/audio_manager_android.cc
@@ -53,7 +53,8 @@ AudioManagerAndroid::AudioManagerAndroid(AudioLogFactory* audio_log_factory)
j_audio_manager_.Reset(
Java_AudioManagerAndroid_createAudioManagerAndroid(
base::android::AttachCurrentThread(),
- base::android::GetApplicationContext()));
+ base::android::GetApplicationContext(),
+ reinterpret_cast<intptr_t>(this)));
Init();
}
@@ -126,6 +127,12 @@ AudioOutputStream* AudioManagerAndroid::MakeAudioOutputStream(
if (stream && output_stream_count() == 1) {
SetAudioMode(kAudioModeInCommunication);
}
+
+ {
+ base::AutoLock lock(streams_lock_);
+ streams_.insert(static_cast<OpenSLESOutputStream*>(stream));
+ }
+
return stream;
}
@@ -141,6 +148,8 @@ void AudioManagerAndroid::ReleaseOutputStream(AudioOutputStream* stream) {
if (!output_stream_count()) {
SetAudioMode(kAudioModeNormal);
}
+ base::AutoLock lock(streams_lock_);
+ streams_.erase(static_cast<OpenSLESOutputStream*>(stream));
}
void AudioManagerAndroid::ReleaseInputStream(AudioInputStream* stream) {
@@ -241,6 +250,23 @@ void AudioManagerAndroid::Close() {
j_audio_manager_.obj());
}
+void AudioManagerAndroid::SetMute(JNIEnv* env, jobject obj, jboolean muted) {
+ GetMessageLoop()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &AudioManagerAndroid::DoSetMuteOnAudioThread,
+ base::Unretained(this),
+ muted));
+}
+
+void AudioManagerAndroid::DoSetMuteOnAudioThread(bool muted) {
+ base::AutoLock lock(streams_lock_);
+ for (OutputStreams::iterator it = streams_.begin();
+ it != streams_.end(); ++it) {
+ (*it)->SetMute(muted);
+ }
+}
+
void AudioManagerAndroid::SetAudioMode(int mode) {
Java_AudioManagerAndroid_setMode(
base::android::AttachCurrentThread(),
diff --git a/media/audio/android/audio_manager_android.h b/media/audio/android/audio_manager_android.h
index 59c830d..d9744ff 100644
--- a/media/audio/android/audio_manager_android.h
+++ b/media/audio/android/audio_manager_android.h
@@ -5,12 +5,17 @@
#ifndef MEDIA_AUDIO_ANDROID_AUDIO_MANAGER_ANDROID_H_
#define MEDIA_AUDIO_ANDROID_AUDIO_MANAGER_ANDROID_H_
+#include <set>
+
#include "base/android/jni_android.h"
#include "base/gtest_prod_util.h"
+#include "base/synchronization/lock.h"
#include "media/audio/audio_manager_base.h"
namespace media {
+class OpenSLESOutputStream;
+
// Android implemention of AudioManager.
class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase {
public:
@@ -52,6 +57,8 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase {
static bool RegisterAudioManager(JNIEnv* env);
+ void SetMute(JNIEnv* env, jobject obj, jboolean muted);
+
protected:
virtual ~AudioManagerAndroid();
@@ -69,12 +76,21 @@ class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase {
int GetAudioLowLatencyOutputFrameSize();
int GetOptimalOutputFrameSize(int sample_rate, int channels);
+ void DoSetMuteOnAudioThread(bool muted);
+
// Allow the AudioAndroidTest to access private methods.
FRIEND_TEST_ALL_PREFIXES(AudioAndroidTest, IsAudioLowLatencySupported);
// Java AudioManager instance.
base::android::ScopedJavaGlobalRef<jobject> j_audio_manager_;
+ typedef std::set<OpenSLESOutputStream*> OutputStreams;
+ OutputStreams streams_;
+ // TODO(wjia): remove this lock once unit test modules are fixed to call
+ // AudioManager::MakeAudioOutputStream on the audio thread. For now, this
+ // lock is used to guard access to |streams_|.
+ base::Lock streams_lock_;
+
DISALLOW_COPY_AND_ASSIGN(AudioManagerAndroid);
};
diff --git a/media/audio/android/opensles_output.cc b/media/audio/android/opensles_output.cc
index 5643f83..77e6778 100644
--- a/media/audio/android/opensles_output.cc
+++ b/media/audio/android/opensles_output.cc
@@ -28,6 +28,7 @@ OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager,
active_buffer_index_(0),
buffer_size_bytes_(0),
started_(false),
+ muted_(false),
volume_(1.0) {
DVLOG(2) << "OpenSLESOutputStream::OpenSLESOutputStream()";
format_.formatType = SL_DATAFORMAT_PCM;
@@ -172,6 +173,12 @@ void OpenSLESOutputStream::GetVolume(double* volume) {
*volume = static_cast<double>(volume_);
}
+void OpenSLESOutputStream::SetMute(bool muted) {
+ DVLOG(2) << "OpenSLESOutputStream::SetMute(" << muted << ")";
+ DCHECK(thread_checker_.CalledOnValidThread());
+ muted_ = muted;
+}
+
bool OpenSLESOutputStream::CreatePlayer() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!engine_object_.Get());
@@ -324,7 +331,7 @@ void OpenSLESOutputStream::FillBufferQueueNoLock() {
// Note: If the internal representation ever changes from 16-bit PCM to
// raw float, the data must be clipped and sanitized since it may come
// from an untrusted source such as NaCl.
- audio_bus_->Scale(volume_);
+ audio_bus_->Scale(muted_ ? 0.0f : volume_);
audio_bus_->ToInterleaved(frames_filled,
format_.bitsPerSample / 8,
audio_data_[active_buffer_index_]);
diff --git a/media/audio/android/opensles_output.h b/media/audio/android/opensles_output.h
index 7232d5d..623b019 100644
--- a/media/audio/android/opensles_output.h
+++ b/media/audio/android/opensles_output.h
@@ -40,6 +40,10 @@ class OpenSLESOutputStream : public AudioOutputStream {
virtual void SetVolume(double volume) OVERRIDE;
virtual void GetVolume(double* volume) OVERRIDE;
+ // Set the value of |muted_|. It does not affect |volume_| which can be
+ // got by calling GetVolume(). See comments for |muted_| below.
+ void SetMute(bool muted);
+
private:
bool CreatePlayer();
@@ -96,6 +100,12 @@ class OpenSLESOutputStream : public AudioOutputStream {
bool started_;
+ // Volume control coming from hardware. It overrides |volume_| when it's
+ // true. Otherwise, use |volume_| for scaling.
+ // This is needed because platform voice volume never goes to zero in
+ // COMMUNICATION mode on Android.
+ bool muted_;
+
// Volume level from 0 to 1.
float volume_;
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 1a9cb0a..0c37c0a 100644
--- a/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
+++ b/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
@@ -7,16 +7,23 @@ package org.chromium.media;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.database.ContentObserver;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
+import android.net.Uri;
import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
import android.os.Process;
+import android.provider.Settings;
+import android.provider.Settings.System;
import android.util.Log;
import java.util.ArrayList;
@@ -106,6 +113,7 @@ class AudioManagerAndroid {
private final AudioManager mAudioManager;
private final Context mContext;
+ private final long mNativeAudioManagerAndroid;
private boolean mHasBluetoothPermission = false;
private boolean mIsInitialized = false;
@@ -121,18 +129,28 @@ class AudioManagerAndroid {
// Contains a list of currently available audio devices.
private boolean[] mAudioDevices = new boolean[DEVICE_COUNT];
+ private final ContentResolver mContentResolver;
+ private SettingsObserver mSettingsObserver = null;
+ private SettingsObserverThread mSettingsObserverThread = null;
+ private int mCurrentVolume;
+ private final Object mSettingsObserverLock = new Object();
+
// Broadcast receiver for wired headset intent broadcasts.
private BroadcastReceiver mWiredHeadsetReceiver;
/** Construction */
@CalledByNative
- private static AudioManagerAndroid createAudioManagerAndroid(Context context) {
- return new AudioManagerAndroid(context);
+ private static AudioManagerAndroid createAudioManagerAndroid(
+ Context context,
+ long nativeAudioManagerAndroid) {
+ return new AudioManagerAndroid(context, nativeAudioManagerAndroid);
}
- private AudioManagerAndroid(Context context) {
+ private AudioManagerAndroid(Context context, long nativeAudioManagerAndroid) {
mContext = context;
+ mNativeAudioManagerAndroid = nativeAudioManagerAndroid;
mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
+ mContentResolver = mContext.getContentResolver();
}
/**
@@ -182,6 +200,16 @@ class AudioManagerAndroid {
initBluetooth();
mIsInitialized = true;
+
+ mSettingsObserverThread = new SettingsObserverThread();
+ mSettingsObserverThread.start();
+ synchronized(mSettingsObserverLock) {
+ try {
+ mSettingsObserverLock.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "unregisterHeadsetReceiver exception: " + e.getMessage());
+ }
+ }
}
/**
@@ -193,6 +221,14 @@ class AudioManagerAndroid {
if (!mIsInitialized)
return;
+ if (mSettingsObserverThread != null ) {
+ mSettingsObserverThread = null;
+ }
+ if (mSettingsObserver != null) {
+ mContentResolver.unregisterContentObserver(mSettingsObserver);
+ mSettingsObserver = null;
+ }
+
unregisterForWiredHeadsetIntentBroadcast();
// Restore previously stored audio states.
@@ -575,4 +611,40 @@ class AudioManagerAndroid {
private void loge(String msg) {
Log.e(TAG, msg);
}
+
+ private class SettingsObserver extends ContentObserver {
+ SettingsObserver() {
+ super(new Handler());
+ mContentResolver.registerContentObserver(Settings.System.CONTENT_URI, true, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ int volume = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
+ nativeSetMute(mNativeAudioManagerAndroid, (volume == 0));
+ }
+ }
+
+ private native void nativeSetMute(long nativeAudioManagerAndroid, boolean muted);
+
+ private class SettingsObserverThread extends Thread {
+ SettingsObserverThread() {
+ super("SettinsObserver");
+ }
+
+ @Override
+ public void run() {
+ // Set this thread up so the handler will work on it.
+ Looper.prepare();
+
+ synchronized(mSettingsObserverLock) {
+ mSettingsObserver = new SettingsObserver();
+ mSettingsObserverLock.notify();
+ }
+
+ // Listen for volume change.
+ Looper.loop();
+ }
+ }
}