Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Unified Diff: media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java

Issue 78033003: Adding device enumeration to Android device manager (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Initial support for BT detection Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 0f0cfb61e08428eb477c9de493d4d60fff342293..faf884abe37c529bd846576a2930516b273f46d0 100644
--- a/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
+++ b/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java
@@ -4,6 +4,8 @@
package org.chromium.media;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -16,6 +18,12 @@ import android.media.AudioTrack;
import android.os.Build;
import android.util.Log;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;
@@ -23,6 +31,39 @@ import org.chromium.base.JNINamespace;
class AudioManagerAndroid {
private static final String TAG = "AudioManagerAndroid";
+ /**
+ * Lists the possible types (and names) of audio device that we support
+ * on Android.
+ */
+ private enum AudioDevice {
+ SPEAKERPHONE("Speaker"),
+ WIRED_HEADSET("Wired headphones"),
+ EARPIECE("Handset earpice"),
+ BLUETOOTH_HEADSET("Bluetooth headset");
+
+ private final String name;
+
+ private AudioDevice(String s) {
+ name = s;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ public AudioDevice fromString(String name) {
+ if (name == null)
+ return null;
+ for (AudioDevice a : AudioDevice.values()) {
+ if (name.equalsIgnoreCase(a.name)) {
+ return a;
+ }
+ }
+ return null;
+ }
+ }
+
// Most of Google lead devices use 44.1K as the default sampling rate, 44.1K
// is also widely used on other android devices.
private static final int DEFAULT_SAMPLING_RATE = 44100;
@@ -31,12 +72,63 @@ class AudioManagerAndroid {
// getProperty(PROPERTY_OUTPUT_FRAMES_PER_BUFFER) fails.
private static final int DEFAULT_FRAME_PER_BUFFER = 256;
+ private boolean mIsInitialized = false;
+
private final AudioManager mAudioManager;
private final Context mContext;
private BroadcastReceiver mReceiver;
private boolean mOriginalSpeakerStatus;
+ // Use Set to ensure that we don't store duplicates. Ignores order.
+ private Set<AudioDevice> mAudioDevices = new HashSet<AudioDevice>();
+
+ /**
+ * Class to handle changes in wired headset availablilty.
+ *
+ * Note that isInitialStickyBroadcast() returns true if the receiver is
+ * currently processing the initial value of a sticky broadcast. It can
+ * happen during startup when the cached state us pushed out to us an
+ * initial "sticky broadcast".
+ */
+ private BroadcastReceiver mWiredHeadsetReceiver = new BroadcastReceiver() {
+ private static final int STATE_UNPLUGGED = 0;
+ private static final int STATE_PLUGGED = 1;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
+ int state = intent.getIntExtra("state", STATE_UNPLUGGED);
+
+ logd("==> WiredHeadsetReceiver.onReceive: state=" + state
+ + ", isInitialStickyBroadcast="
+ + isInitialStickyBroadcast());
+
+ switch (state) {
+ case STATE_UNPLUGGED:
+ // Wired headset and earpiece are mutually-exclusive.
+ mAudioDevices.remove(AudioDevice.WIRED_HEADSET);
+ if (hasEarpiece()) {
+ mAudioDevices.add(AudioDevice.EARPIECE);
+ }
+ // TODO(henrika): check if wired headset was used before
+ // it was unlpugged. If so, switch to speaker phone.
+ // If'it was not in use, don't do anything.
+ // Also, turn off BT if it was active at this stage.
+ break;
+ case STATE_PLUGGED:
+ // Wired headset and earpiece are mutually-exclusive.
+ mAudioDevices.add(AudioDevice.WIRED_HEADSET);
+ mAudioDevices.remove(AudioDevice.EARPIECE);
+ // TODO(henrika): ensure that the wired headset is active.
+ // Possibly turn BT off and also disable the speaker phone.
+ break;
+ }
+ }
+ }
+ };
+
@CalledByNative
public void setMode(int mode) {
try {
@@ -57,6 +149,51 @@ class AudioManagerAndroid {
mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
}
+ /**
+ * Pupulate the list of available audio devices and register receivers
+ * for broadcasted intents related to wired headset and bluetooth devices.
+ * TODO(henrika): we should probably store an initial entry state here
+ * as well.
+ */
+ @CalledByNative
+ public void init() {
+ if (mIsInitialized)
+ return;
+
+ // Initialize audio device list with things we know is always available.
+ if (hasEarpiece()) {
+ mAudioDevices.add(AudioDevice.EARPIECE);
+ }
+ mAudioDevices.add(AudioDevice.SPEAKERPHONE);
+
+ registerForWiredHeadsetIntentBroadcast();
+ initBluetooth();
+
+ logd("init: devices=" + mAudioDevices);
+ mIsInitialized = true;
+ }
+
+ /**
+ * Unregister all previously registered intent receivers.
+ * TODO(henrika): we should probably restore the initial entry state here
+ * as well.
+ */
+ @CalledByNative
+ public void close() {
+ if (!mIsInitialized)
+ return;
+
+ logd("close");
+ unregisterForWiredHeadsetIntentBroadcast();
+ mIsInitialized = false;
+ }
+
+ @CalledByNative
+ public String[] getAudioOutputDeviceNames() {
+ logd("getAudioOutputDeviceNames");
+ return toStringArray(mAudioDevices);
+ }
+
@CalledByNative
public void registerHeadsetReceiver() {
if (mReceiver != null) {
@@ -165,4 +302,101 @@ class AudioManagerAndroid {
DEFAULT_FRAME_PER_BUFFER : Integer.parseInt(framesPerBuffer));
}
+ /**
+ * Returns true if the device has a headset earpice and false if not.
+ */
+ private boolean hasEarpiece() {
+ boolean hasFeature = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY);
+ return hasFeature;
+ }
+
+ /**
+ * Converts a set of audio devices into array of strings.
+ */
+ private static String[] toStringArray(Set<AudioDevice> devices) {
+ if (devices == null) {
+ return null;
+ }
+ String[] retVal = new String[devices.size()];
+ int i = 0;
+ for (AudioDevice device : devices) {
+ retVal[i] = device.toString();
+ i++;
+ }
+ return retVal;
+ }
+
+ /**
+ * Registers receiver for the broadcasted intent when a Wired Headset is
+ * plugged in or unplugged. The received intent will have an extra
+ * 'state' value where 0 means unplugged, and 1 means plugged.
+ */
+ private void registerForWiredHeadsetIntentBroadcast() {
+ logd("registerForWiredHeadsetIntentBroadcast");
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_HEADSET_PLUG);
+ mContext.registerReceiver(mWiredHeadsetReceiver, filter);
+ // Note: the intent we just registered for is sticky, so it'll tell us
+ // immediately what the last action was (plugged or unplugged).
+ // It will enable us to set the speakerphone correctly.
+ }
+
+ /**
+ * Unregister receiver for broadcasted ACTION_HEADSET_PLUG intent.
+ */
+ private void unregisterForWiredHeadsetIntentBroadcast() {
+ logd("unregisterForWiredHeadsetIntentBroadcast");
+ mContext.unregisterReceiver(mWiredHeadsetReceiver);
+ mWiredHeadsetReceiver = null;
+ }
+
+
+ /**
+ * 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.
+ */
+ private void initBluetooth() {
+ logd("initBluetooth");
+
+ // 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.BluetoothProfile.STATE_CONNECTED ==
+ btAdapter.getProfileConnectionState(
+ android.bluetooth.BluetoothProfile.HEADSET)) {
+ logd("BT device was connected at start of call");
+ mAudioDevices.add(AudioDevice.BLUETOOTH_HEADSET);
+ // TODO(henrika): ensure that we set the active audio
+ // device to Bluetooth (not trivial).
+ // setAudioDevice(AudioDevice.BLUETOOTH_HEADSET);
+ }
+ }
+
+ /**
+ * Trivial helper method for debug logging.
+ */
+ private void logd(String msg) {
+ Log.d(TAG, msg);
+ }
}

Powered by Google App Engine
This is Rietveld 408576698