Audio routing part 2
- Added WIRED_HEADSET and SPEAKER to AudioModes
- Added WiredHeadsetManager class
- AudioRouter now listens to WiredHeadsetManager and PhoneUtils for
speakerphone
- AudioRouter maintains state across earpiece,headset,bluetooth,speaker
- Most code copied from InCallScreen for speaker logic
- CallHandlerService listens to audioRouter for audio states
- Moved Wired headset logic from phoneglobals to wiredheadsetmanager
- Better toString for logging Call objects
Change-Id: Iebc8c83934ce5eff6f1918b0610804fadb163b43
diff --git a/common/Android.mk b/common/Android.mk
index 972ed39..2667e45 100644
--- a/common/Android.mk
+++ b/common/Android.mk
@@ -16,6 +16,8 @@
include $(CLEAR_VARS)
+LOCAL_STATIC_JAVA_LIBRARIES := guava
+
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
$(call all-Iaidl-files-under, src)
diff --git a/common/src/com/android/services/telephony/common/AudioMode.java b/common/src/com/android/services/telephony/common/AudioMode.java
index f296a93..b0043ef 100644
--- a/common/src/com/android/services/telephony/common/AudioMode.java
+++ b/common/src/com/android/services/telephony/common/AudioMode.java
@@ -21,10 +21,13 @@
*/
public class AudioMode {
// These can be used as a bit mask
- public static int EARPIECE = 0x00000001;
- public static int BLUETOOTH = 0x00000002;
+ public static int EARPIECE = 0x00000001;
+ public static int BLUETOOTH = 0x00000002;
+ public static int WIRED_HEADSET = 0x00000004;
+ public static int SPEAKER = 0x00000008;
- public static int ALL_MODES = EARPIECE | BLUETOOTH;
+ public static int WIRED_OR_EARPIECE = EARPIECE | WIRED_HEADSET;
+ public static int ALL_MODES = EARPIECE | BLUETOOTH | WIRED_HEADSET | SPEAKER;
public static String toString(int mode) {
if ((mode & ~ALL_MODES) != 0x0) {
@@ -38,6 +41,12 @@
if ((mode & BLUETOOTH) == BLUETOOTH) {
listAppend(buffer, "BLUETOOTH");
}
+ if ((mode & WIRED_HEADSET) == WIRED_HEADSET) {
+ listAppend(buffer, "WIRED_HEADSET");
+ }
+ if ((mode & SPEAKER) == SPEAKER) {
+ listAppend(buffer, "SPEAKER");
+ }
return buffer.toString();
}
diff --git a/common/src/com/android/services/telephony/common/Call.java b/common/src/com/android/services/telephony/common/Call.java
index 68b493f..a3ba003 100644
--- a/common/src/com/android/services/telephony/common/Call.java
+++ b/common/src/com/android/services/telephony/common/Call.java
@@ -16,9 +16,14 @@
package com.android.services.telephony.common;
+import com.google.common.collect.ImmutableMap;
+
import android.os.Parcel;
import android.os.Parcelable;
+import java.nio.Buffer;
+import java.util.Map;
+
import com.android.internal.telephony.PhoneConstants;
/**
@@ -52,6 +57,16 @@
public static final int ONHOLD = 6;
}
+ private static final Map<Integer, String> STATE_MAP = ImmutableMap.<Integer, String>builder()
+ .put(Call.State.ACTIVE, "ACTIVE")
+ .put(Call.State.CALL_WAITING, "CALL_WAITING")
+ .put(Call.State.DIALING, "DIALING")
+ .put(Call.State.IDLE, "IDLE")
+ .put(Call.State.INCOMING, "INCOMING")
+ .put(Call.State.ONHOLD, "ONHOLD")
+ .put(Call.State.INVALID, "INVALID")
+ .build();
+
// Number presentation type for caller id display
// normal
public static int PRESENTATION_ALLOWED = PhoneConstants.PRESENTATION_ALLOWED;
@@ -157,4 +172,13 @@
mCnapName = in.readString();
}
+ @Override
+ public String toString() {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("callId: ");
+ buffer.append(mCallId);
+ buffer.append(", state: ");
+ buffer.append(STATE_MAP.get(mState));
+ return buffer.toString();
+ }
}
diff --git a/common/src/com/android/services/telephony/common/ICallHandlerService.aidl b/common/src/com/android/services/telephony/common/ICallHandlerService.aidl
index 33055c5..4b6f913 100644
--- a/common/src/com/android/services/telephony/common/ICallHandlerService.aidl
+++ b/common/src/com/android/services/telephony/common/ICallHandlerService.aidl
@@ -61,5 +61,5 @@
* Called when the supported audio modes change.
* {@see AudioMode}
*/
- void onAudioModeSupportChange(in int modeMask);
+ void onSupportedAudioModeChange(in int modeMask);
}
diff --git a/src/com/android/phone/AudioRouter.java b/src/com/android/phone/AudioRouter.java
index 2b8201c..812d92c 100644
--- a/src/com/android/phone/AudioRouter.java
+++ b/src/com/android/phone/AudioRouter.java
@@ -19,11 +19,14 @@
import com.google.common.collect.Lists;
import android.content.Context;
-import android.media.AudioManager;
import android.os.SystemProperties;
+import android.provider.MediaStore.Audio;
import android.util.Log;
+import com.android.internal.telephony.CallManager;
+import com.android.internal.telephony.PhoneConstants;
import com.android.phone.BluetoothManager.BluetoothIndicatorListener;
+import com.android.phone.WiredHeadsetManager.WiredHeadsetListener;
import com.android.services.telephony.common.AudioMode;
import java.util.List;
@@ -31,7 +34,7 @@
/**
* Responsible for Routing in-call audio and maintaining routing state.
*/
-/* package */ class AudioRouter implements BluetoothIndicatorListener {
+/* package */ class AudioRouter implements BluetoothIndicatorListener, WiredHeadsetListener {
private static String LOG_TAG = AudioRouter.class.getSimpleName();
private static final boolean DBG =
@@ -40,13 +43,19 @@
private final Context mContext;
private final BluetoothManager mBluetoothManager;
+ private final WiredHeadsetManager mWiredHeadsetManager;
+ private final CallManager mCallManager;
private final List<AudioModeListener> mListeners = Lists.newArrayList();
private int mAudioMode = AudioMode.EARPIECE;
private int mPreviousMode = AudioMode.EARPIECE;
+ private int mSupportedModes = AudioMode.ALL_MODES;
- public AudioRouter(Context context, BluetoothManager bluetoothManager) {
+ public AudioRouter(Context context, BluetoothManager bluetoothManager,
+ WiredHeadsetManager wiredHeadsetManager, CallManager callManager) {
mContext = context;
mBluetoothManager = bluetoothManager;
+ mWiredHeadsetManager = wiredHeadsetManager;
+ mCallManager = callManager;
init();
}
@@ -59,11 +68,22 @@
}
/**
+ * Returns the currently supported audio modes.
+ */
+ public int getSupportedAudioModes() {
+ return mSupportedModes;
+ }
+
+ /**
* Add a listener to audio mode changes.
*/
public void addAudioModeListener(AudioModeListener listener) {
if (!mListeners.contains(listener)) {
mListeners.add(listener);
+
+ // For first notification, mPreviousAudioMode doesn't make sense.
+ listener.onAudioModeChange(mAudioMode, mAudioMode);
+ listener.onSupportedAudioModeChange(mSupportedModes);
}
}
@@ -80,13 +100,90 @@
* Sets the audio mode to the mode that is passed in.
*/
public void setAudioMode(int mode) {
- if (AudioMode.BLUETOOTH == mode) {
+ logD("setAudioMode " + AudioMode.toString(mode));
- // dont set mAudioMode because we will get a notificaiton through
- // onBluetoothIndicationChange if successful
- toggleBluetooth(true);
- } else if (AudioMode.EARPIECE == mode) {
- toggleBluetooth(false);
+ // Since they are mutually exclusive and one is ALWAYS valid, we allow a special input of
+ // WIRED_OR_EARPIECE so that callers dont have to make a call to check which is supported
+ // before calling setAudioMode.
+ if (mode == AudioMode.WIRED_OR_EARPIECE) {
+ mode = AudioMode.WIRED_OR_EARPIECE & mSupportedModes;
+
+ if (mode == 0) {
+ Log.wtf(LOG_TAG, "One of wired headset or earpiece should always be valid.");
+ // assume earpiece in this case.
+ mode = AudioMode.EARPIECE;
+ }
+ }
+
+ if ((calculateSupportedModes() | mode) == 0) {
+ Log.wtf(LOG_TAG, "Asking to set to a mode that is unsupported: " + mode);
+ return;
+ }
+
+ if (AudioMode.SPEAKER == mode) {
+
+ if (!PhoneUtils.isSpeakerOn(mContext)) {
+ // Switch away from Bluetooth, if it was active.
+ if (mBluetoothManager.isBluetoothAvailable() &&
+ mBluetoothManager.isBluetoothAudioConnected()) {
+
+ mBluetoothManager.disconnectBluetoothAudio();
+ }
+ PhoneUtils.turnOnSpeaker(mContext, true, true);
+ }
+
+ } else if (AudioMode.BLUETOOTH == mode) {
+
+ // If already connected to BT, there's nothing to do here.
+ if (mBluetoothManager.isBluetoothAvailable() &&
+ !mBluetoothManager.isBluetoothAudioConnected()) {
+ // Manually turn the speaker phone off, instead of allowing the
+ // Bluetooth audio routing to handle it, since there's other
+ // important state-updating that needs to happen in the
+ // PhoneUtils.turnOnSpeaker() method.
+ // (Similarly, whenever the user turns *on* the speaker, we
+ // manually disconnect the active bluetooth headset;
+ // see toggleSpeaker() and/or switchInCallAudio().)
+ if (PhoneUtils.isSpeakerOn(mContext)) {
+ PhoneUtils.turnOnSpeaker(mContext, false, true);
+ }
+ mBluetoothManager.connectBluetoothAudio();
+ }
+
+ // Wired headset and earpiece work the same way
+ } else if (AudioMode.EARPIECE == mode || AudioMode.WIRED_HEADSET == mode) {
+
+ // Switch to either the handset earpiece, or the wired headset (if connected.)
+ // (Do this by simply making sure both speaker and bluetooth are off.)
+ if (mBluetoothManager.isBluetoothAvailable() &&
+ mBluetoothManager.isBluetoothAudioConnected()) {
+ mBluetoothManager.disconnectBluetoothAudio();
+ }
+ if (PhoneUtils.isSpeakerOn(mContext)) {
+ PhoneUtils.turnOnSpeaker(mContext, false, true);
+ }
+
+ } else {
+ Log.wtf(LOG_TAG, "Asking us to set to an unsupported mode " +
+ AudioMode.toString(mode) + " (" + mode + ")");
+
+ // set it to the current audio mode
+ mode = mAudioMode;
+ }
+
+ updateAudioModeTo(mode);
+ }
+
+ /**
+ * Turns on speaker.
+ */
+ public void setSpeaker(boolean on) {
+ logD("setSpeaker " + on);
+
+ if (on) {
+ setAudioMode(AudioMode.SPEAKER);
+ } else {
+ setAudioMode(AudioMode.WIRED_OR_EARPIECE);
}
}
@@ -96,47 +193,150 @@
*/
@Override
public void onBluetoothIndicationChange(boolean isConnected, BluetoothManager btManager) {
- int newMode = mAudioMode;
+ logD("onBluetoothIndicationChange " + isConnected);
- if (isConnected) {
- newMode = AudioMode.BLUETOOTH;
- } else {
- newMode = AudioMode.EARPIECE;
- }
-
- changeAudioModeTo(newMode);
+ // this will read the new bluetooth mode appropriately
+ updateAudioModeTo(calculateModeFromCurrentState());
}
- private void toggleBluetooth(boolean on) {
- if (on) {
- mBluetoothManager.connectBluetoothAudio();
- } else {
- mBluetoothManager.disconnectBluetoothAudio();
+ /**
+ * Called when the state of the wired headset changes.
+ */
+ @Override
+ public void onWiredHeadsetConnection(boolean pluggedIn) {
+ logD("onWireHeadsetConnection " + pluggedIn);
+
+ // Since the presence of a wired headset or bluetooth affects the
+ // speakerphone, update the "speaker" state. We ONLY want to do
+ // this on the wired headset connect / disconnect events for now
+ // though.
+ PhoneConstants.State phoneState = mCallManager.getState();
+
+ int newMode = mAudioMode;
+
+ // TODO: Consider using our stored states instead
+
+ // Do not change speaker state if phone is not off hook
+ if (phoneState == PhoneConstants.State.OFFHOOK &&
+ !mBluetoothManager.isBluetoothHeadsetAudioOn()) {
+ if (!pluggedIn) {
+ // if the state is "not connected", restore the speaker state.
+ PhoneUtils.restoreSpeakerMode(mContext);
+
+ if (PhoneUtils.isSpeakerOn(mContext)) {
+ newMode = AudioMode.SPEAKER;
+ } else {
+ newMode = AudioMode.EARPIECE;
+ }
+ } else {
+ // if the state is "connected", force the speaker off without
+ // storing the state.
+ PhoneUtils.turnOnSpeaker(mContext, false, false);
+
+ newMode = AudioMode.WIRED_HEADSET;
+ }
}
+
+ updateAudioModeTo(newMode);
}
private void init() {
mBluetoothManager.addBluetoothIndicatorListener(this);
+ mWiredHeadsetManager.addWiredHeadsetListener(this);
}
- private void changeAudioModeTo(int mode) {
+ /**
+ * Reads the state of the world to determine Audio mode.
+ */
+ private int calculateModeFromCurrentState() {
+
+ int mode = AudioMode.EARPIECE;
+
+ // There is a very specific ordering here
+ if (mBluetoothManager.isBluetoothAudioConnectedOrPending()) {
+ mode = AudioMode.BLUETOOTH;
+ } else if (PhoneUtils.isSpeakerOn(mContext)) {
+ mode = AudioMode.SPEAKER;
+ } else if (mWiredHeadsetManager.isHeadsetPlugged()) {
+ mode = AudioMode.WIRED_HEADSET;
+ }
+
+ logD("calculateModeFromCurrentState " + AudioMode.toString(mode));
+
+ return mode;
+ }
+
+ /**
+ * Changes the audio mode to the mode in the parameter.
+ */
+ private void updateAudioModeTo(int mode) {
+ int oldSupportedModes = mSupportedModes;
+
+ mSupportedModes = calculateSupportedModes();
+
+ // This is a weird state that shouldn't happen, but we get called here
+ // once we've changed the audio layers so lets log the error, but assume
+ // that it went through. If it happens it is likely it is a race condition
+ // that will resolve itself when we get updates on the mode change.
+ if ((mSupportedModes & mode) == 0) {
+ Log.e(LOG_TAG, "Setting audio mode to an unsupported mode!");
+ }
+
+ boolean doNotify = oldSupportedModes != mSupportedModes;
+
+ // only update if it really changed.
if (mAudioMode != mode) {
Log.i(LOG_TAG, "Audio mode changing to " + AudioMode.toString(mode));
mPreviousMode = mAudioMode;
mAudioMode = mode;
+ doNotify = true;
+ }
+
+ if (doNotify) {
notifyListeners();
}
}
+ /**
+ * Gets the updates supported modes from the state of the audio systems.
+ */
+ private int calculateSupportedModes() {
+ // speaker phone always a supported state
+ int supportedModes = AudioMode.SPEAKER;
+
+ if (mWiredHeadsetManager.isHeadsetPlugged()) {
+ supportedModes |= AudioMode.WIRED_HEADSET;
+ } else {
+ supportedModes |= AudioMode.EARPIECE;
+ }
+
+ if (mBluetoothManager.isBluetoothAvailable()) {
+ supportedModes |= AudioMode.BLUETOOTH;
+ }
+
+ return supportedModes;
+ }
+
private void notifyListeners() {
+ logD("AudioMode: " + AudioMode.toString(mAudioMode));
+ logD("Supported AudioMode: " + AudioMode.toString(mSupportedModes));
+
for (int i = 0; i < mListeners.size(); i++) {
mListeners.get(i).onAudioModeChange(mPreviousMode, mAudioMode);
+ mListeners.get(i).onSupportedAudioModeChange(mSupportedModes);
}
}
public interface AudioModeListener {
void onAudioModeChange(int previousMode, int newMode);
+ void onSupportedAudioModeChange(int modeMask);
+ }
+
+ private void logD(String msg) {
+ if (DBG) {
+ Log.d(LOG_TAG, msg);
+ }
}
}
diff --git a/src/com/android/phone/BluetoothManager.java b/src/com/android/phone/BluetoothManager.java
index 8d20c25..6f26b95 100644
--- a/src/com/android/phone/BluetoothManager.java
+++ b/src/com/android/phone/BluetoothManager.java
@@ -38,13 +38,12 @@
import java.util.List;
/**
- * Listens to and caches bluetooth headset state. Used By the CallHandlerServiceProxy
- * for sending this state to the UI layer. Also provides method for connection the bluetooth
- * headset to the phone call. This is used by the CallCommandService to allow the UI to use the
- * bluetooth headset if connected.
+ * Listens to and caches bluetooth headset state. Used By the AudioRouter for maintaining
+ * overall audio state for use in the UI layer. Also provides method for connecting the bluetooth
+ * headset to the phone call.
*/
public class BluetoothManager {
- private static final String LOG_TAG = "InCallScreen";
+ private static final String LOG_TAG = BluetoothManager.class.getSimpleName();
private static final boolean DBG =
(PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
private static final boolean VDBG = (PhoneGlobals.DBG_LEVEL >= 2);
diff --git a/src/com/android/phone/CallHandlerServiceProxy.java b/src/com/android/phone/CallHandlerServiceProxy.java
index 165e693..ad69d80 100644
--- a/src/com/android/phone/CallHandlerServiceProxy.java
+++ b/src/com/android/phone/CallHandlerServiceProxy.java
@@ -48,19 +48,22 @@
(PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
- private Context mContext;
- private CallModeler mCallModeler;
- private ServiceConnection mConnection;
- private ICallHandlerService mCallHandlerService;
+ private AudioRouter mAudioRouter;
private CallCommandService mCallCommandService;
+ private CallModeler mCallModeler;
+ private Context mContext;
+ private ICallHandlerService mCallHandlerService;
+ private ServiceConnection mConnection;
public CallHandlerServiceProxy(Context context, CallModeler callModeler,
- CallCommandService callCommandService) {
+ CallCommandService callCommandService, AudioRouter audioRouter) {
mContext = context;
mCallCommandService = callCommandService;
mCallModeler = callModeler;
+ mAudioRouter = audioRouter;
setupServiceConnection();
+ mAudioRouter.addAudioModeListener(this);
mCallModeler.addListener(this);
// start the whole process
@@ -108,6 +111,8 @@
if (mCallHandlerService != null) {
try {
+ if (DBG) Log.d(TAG, "onSupportAudioModeChange");
+
mCallHandlerService.onAudioModeChange(newMode);
} catch (RemoteException e) {
Log.e(TAG, "Remote exception handling onAudioModeChange", e);
@@ -115,6 +120,19 @@
}
}
+ @Override
+ public void onSupportedAudioModeChange(int modeMask) {
+ if (mCallHandlerService != null) {
+ try {
+ if (DBG) Log.d(TAG, "onSupportAudioModeChange: " + AudioMode.toString(modeMask));
+
+ mCallHandlerService.onSupportedAudioModeChange(modeMask);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote exception handling onAudioModeChange", e);
+ }
+ }
+ }
+
/**
* Sets up the connection with ICallHandlerService
*/
diff --git a/src/com/android/phone/InCallScreen.java b/src/com/android/phone/InCallScreen.java
index f20406b..8f52f29 100644
--- a/src/com/android/phone/InCallScreen.java
+++ b/src/com/android/phone/InCallScreen.java
@@ -2766,119 +2766,14 @@
PhoneUtils.setMute(newMuteState);
}
- /**
- * Toggles whether or not to route in-call audio to the bluetooth
- * headset, or do nothing (but log a warning) if no bluetooth device
- * is actually connected.
- *
- * TODO: this method is currently unused, but the "audio mode" UI
- * design is still in flux so let's keep it around for now.
- * (But if we ultimately end up *not* providing any way for the UI to
- * simply "toggle bluetooth", we can get rid of this method.)
- */
public void toggleBluetooth() {
- if (VDBG) log("toggleBluetooth()...");
-
- // TODO(klp): Copy to new AudioManager for new incall
-
- /*
- if (isBluetoothAvailable()) {
- // Toggle the bluetooth audio connection state:
- if (isBluetoothAudioConnected()) {
- disconnectBluetoothAudio();
- } else {
- // Manually turn the speaker phone off, instead of allowing the
- // Bluetooth audio routing to handle it, since there's other
- // important state-updating that needs to happen in the
- // PhoneUtils.turnOnSpeaker() method.
- // (Similarly, whenever the user turns *on* the speaker, we
- // manually disconnect the active bluetooth headset;
- // see toggleSpeaker() and/or switchInCallAudio().)
- if (PhoneUtils.isSpeakerOn(this)) {
- PhoneUtils.turnOnSpeaker(this, false, true);
- }
-
- connectBluetoothAudio();
- }
- } else {
- // Bluetooth isn't available; the onscreen UI shouldn't have
- // allowed this request in the first place!
- Log.w(LOG_TAG, "toggleBluetooth(): bluetooth is unavailable");
- }
-
- // And update the InCallTouchUi widget (since the "audio mode"
- // button might need to change its appearance based on the new
- // audio state.)
- updateInCallTouchUi();
- */
+ // TODO(klp) this still here to avoid compile errors until remove
+ // the UI from services/Telephony completely.
}
- /**
- * Switches the current routing of in-call audio between speaker,
- * bluetooth, and the built-in earpiece (or wired headset.)
- *
- * This method is used on devices that provide a single 3-way switch
- * for audio routing. For devices that provide separate toggles for
- * Speaker and Bluetooth, see toggleBluetooth() and toggleSpeaker().
- *
- * TODO: UI design is still in flux. If we end up totally
- * eliminating the concept of Speaker and Bluetooth toggle buttons,
- * we can get rid of toggleBluetooth() and toggleSpeaker().
- */
public void switchInCallAudio(InCallAudioMode newMode) {
- // TODO(klp): Copy to new AudioManager for new incall
- /*
-
- log("switchInCallAudio: new mode = " + newMode);
- switch (newMode) {
- case SPEAKER:
- if (!PhoneUtils.isSpeakerOn(this)) {
- // Switch away from Bluetooth, if it was active.
- if (isBluetoothAvailable() && isBluetoothAudioConnected()) {
- disconnectBluetoothAudio();
- }
- PhoneUtils.turnOnSpeaker(this, true, true);
- }
- break;
-
- case BLUETOOTH:
- // If already connected to BT, there's nothing to do here.
- if (isBluetoothAvailable() && !isBluetoothAudioConnected()) {
- // Manually turn the speaker phone off, instead of allowing the
- // Bluetooth audio routing to handle it, since there's other
- // important state-updating that needs to happen in the
- // PhoneUtils.turnOnSpeaker() method.
- // (Similarly, whenever the user turns *on* the speaker, we
- // manually disconnect the active bluetooth headset;
- // see toggleSpeaker() and/or switchInCallAudio().)
- if (PhoneUtils.isSpeakerOn(this)) {
- PhoneUtils.turnOnSpeaker(this, false, true);
- }
- connectBluetoothAudio();
- }
- break;
-
- case EARPIECE:
- // Switch to either the handset earpiece, or the wired headset (if connected.)
- // (Do this by simply making sure both speaker and bluetooth are off.)
- if (isBluetoothAvailable() && isBluetoothAudioConnected()) {
- disconnectBluetoothAudio();
- }
- if (PhoneUtils.isSpeakerOn(this)) {
- PhoneUtils.turnOnSpeaker(this, false, true);
- }
- break;
-
- default:
- Log.wtf(LOG_TAG, "switchInCallAudio: unexpected mode " + newMode);
- break;
- }
-
- // And finally, update the InCallTouchUi widget (since the "audio
- // mode" button might need to change its appearance based on the
- // new audio state.)
- updateInCallTouchUi();
- */
+ // TODO(klp) this still here to avoid compile errors until remove
+ // the UI from services/Telephony completely.
}
/**
diff --git a/src/com/android/phone/InCallTouchUi.java b/src/com/android/phone/InCallTouchUi.java
index 993717e..59f2f32 100644
--- a/src/com/android/phone/InCallTouchUi.java
+++ b/src/com/android/phone/InCallTouchUi.java
@@ -867,7 +867,10 @@
// depending on whether a wired headset is physically plugged in.
MenuItem earpieceItem = menu.findItem(R.id.audio_mode_earpiece);
MenuItem wiredHeadsetItem = menu.findItem(R.id.audio_mode_wired_headset);
- final boolean usingHeadset = mApp.isHeadsetPlugged();
+
+ // TODO(klp): This is a compile stop-gap. This will all be deleted
+ final boolean usingHeadset = false; //mApp.isHeadsetPlugged();
+
earpieceItem.setVisible(!usingHeadset);
earpieceItem.setEnabled(!usingHeadset);
wiredHeadsetItem.setVisible(usingHeadset);
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 3e81175..b09789a 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -69,14 +69,18 @@
import com.android.phone.common.CallLogAsync;
import com.android.phone.BluetoothManager.BluetoothIndicatorListener;
import com.android.phone.OtaUtils.CdmaOtaScreenState;
+import com.android.phone.WiredHeadsetManager.WiredHeadsetListener;
import com.android.server.sip.SipService;
+import com.android.services.telephony.common.AudioMode;
/**
* Global state for the telephony subsystem when running in the primary
* phone process.
*/
public class PhoneGlobals extends ContextWrapper
- implements AccelerometerListener.OrientationListener, BluetoothIndicatorListener {
+ implements AccelerometerListener.OrientationListener,
+ BluetoothIndicatorListener,
+ WiredHeadsetListener {
/* package */ static final String LOG_TAG = "PhoneApp";
/**
@@ -103,7 +107,6 @@
// Message codes; see mHandler below.
private static final int EVENT_SIM_NETWORK_LOCKED = 3;
- private static final int EVENT_WIRED_HEADSET_PLUG = 7;
private static final int EVENT_SIM_STATE_CHANGED = 8;
private static final int EVENT_UPDATE_INCALL_NOTIFICATION = 9;
private static final int EVENT_DATA_ROAMING_DISCONNECTED = 10;
@@ -179,6 +182,7 @@
private RejectWithTextMessageManager rejectWithTextMessageManager;
private IBluetoothHeadsetPhone mBluetoothPhone;
private Ringer ringer;
+ private WiredHeadsetManager wiredHeadsetManager;
static int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
static boolean sVoiceCapable = true;
@@ -200,11 +204,6 @@
private boolean mIsSimPinEnabled;
private String mCachedSimPin;
- // True if a wired headset is currently plugged in, based on the state
- // from the latest Intent.ACTION_HEADSET_PLUG broadcast we received in
- // mReceiver.onReceive().
- private boolean mIsHeadsetPlugged;
-
// True if the keyboard is currently *not* hidden
// Gets updated whenever there is a Configuration change
private boolean mIsHardKeyboardOpen;
@@ -337,34 +336,6 @@
PhoneUtils.cancelMmiCode(phone);
break;
- case EVENT_WIRED_HEADSET_PLUG:
- // Since the presence of a wired headset or bluetooth affects the
- // speakerphone, update the "speaker" state. We ONLY want to do
- // this on the wired headset connect / disconnect events for now
- // though, so we're only triggering on EVENT_WIRED_HEADSET_PLUG.
-
- phoneState = mCM.getState();
- // Do not change speaker state if phone is not off hook
- if (phoneState == PhoneConstants.State.OFFHOOK &&
- !bluetoothManager.isBluetoothHeadsetAudioOn()) {
- if (!isHeadsetPlugged()) {
- // if the state is "not connected", restore the speaker state.
- PhoneUtils.restoreSpeakerMode(getApplicationContext());
- } else {
- // if the state is "connected", force the speaker off without
- // storing the state.
- PhoneUtils.turnOnSpeaker(getApplicationContext(), false, false);
- }
- }
- // Update the Proximity sensor based on headset state
- updateProximitySensorMode(phoneState);
-
- // Force TTY state update according to new headset state
- if (mTtyEnabled) {
- sendMessage(obtainMessage(EVENT_TTY_PREFERRED_MODE_CHANGED, 0));
- }
- break;
-
case EVENT_SIM_STATE_CHANGED:
// Marks the event where the SIM goes into ready state.
// Right now, this is only used for the PUK-unlocking
@@ -400,7 +371,10 @@
phoneState = mCM.getState();
if (phoneState == PhoneConstants.State.OFFHOOK &&
- !isHeadsetPlugged() && !bluetoothManager.isBluetoothHeadsetAudioOn()) {
+ !wiredHeadsetManager.isHeadsetPlugged() &&
+ !bluetoothManager.isBluetoothHeadsetAudioOn()) {
+ audioRouter.setSpeaker(inDockMode);
+
PhoneUtils.turnOnSpeaker(getApplicationContext(), inDockMode, true);
updateInCallScreen(); // Has no effect if the InCallScreen isn't visible
}
@@ -409,7 +383,7 @@
case EVENT_TTY_PREFERRED_MODE_CHANGED:
// TTY mode is only applied if a headset is connected
int ttyMode;
- if (isHeadsetPlugged()) {
+ if (wiredHeadsetManager.isHeadsetPlugged()) {
ttyMode = mPreferredTtyMode;
} else {
ttyMode = Phone.TTY_MODE_OFF;
@@ -555,8 +529,12 @@
// Plays DTMF Tones
dtmfTonePlayer = new DTMFTonePlayer(mCM, callModeler);
+ // Manages wired headset state
+ wiredHeadsetManager = new WiredHeadsetManager(this);
+ wiredHeadsetManager.addWiredHeadsetListener(this);
+
// Audio router
- audioRouter = new AudioRouter(this, bluetoothManager);
+ audioRouter = new AudioRouter(this, bluetoothManager, wiredHeadsetManager, mCM);
// Service used by in-call UI to control calls
callCommandService = new CallCommandService(this, mCM, callModeler, dtmfTonePlayer,
@@ -564,7 +542,7 @@
// Sends call state to the UI
callHandlerServiceProxy = new CallHandlerServiceProxy(this, callModeler,
- callCommandService);
+ callCommandService, audioRouter);
// Create the CallNotifer singleton, which handles
// asynchronous events from the telephony layer (like
@@ -593,7 +571,6 @@
IntentFilter intentFilter =
new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
- intentFilter.addAction(Intent.ACTION_HEADSET_PLUG);
intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
intentFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
intentFilter.addAction(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED);
@@ -721,6 +698,14 @@
return bluetoothManager;
}
+ /* package */ WiredHeadsetManager getWiredHeadsetManager() {
+ return wiredHeadsetManager;
+ }
+
+ /* package */ AudioRouter getAudioRouter() {
+ return audioRouter;
+ }
+
/**
* Returns an Intent that can be used to go to the "Call log"
* UI (aka CallLogActivity) in the Contacts app.
@@ -1176,7 +1161,7 @@
// turn proximity sensor off and turn screen on immediately if
// we are using a headset, the keyboard is open, or the device
// is being held in a horizontal position.
- boolean screenOnImmediately = (isHeadsetPlugged()
+ boolean screenOnImmediately = (wiredHeadsetManager.isHeadsetPlugged()
|| PhoneUtils.isSpeakerOn(this)
|| bluetoothManager.isBluetoothHeadsetAudioOn()
|| mIsHardKeyboardOpen);
@@ -1371,15 +1356,6 @@
/**
- * @return true if a wired headset is currently plugged in.
- *
- * @see Intent.ACTION_HEADSET_PLUG (which we listen for in mReceiver.onReceive())
- */
- boolean isHeadsetPlugged() {
- return mIsHeadsetPlugged;
- }
-
- /**
* This needs to be called any time the bluetooth headset state or the
* telephony state changes.
* TODO(klp): See about a ProximityManager-type class listening to bluetooth
@@ -1393,6 +1369,22 @@
}
/**
+ * This is called when the wired headset state changes.
+ */
+ @Override
+ public void onWiredHeadsetConnection(boolean pluggedIn) {
+ PhoneConstants.State phoneState = mCM.getState();
+
+ // Update the Proximity sensor based on headset state
+ updateProximitySensorMode(phoneState);
+
+ // Force TTY state update according to new headset state
+ if (mTtyEnabled) {
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_TTY_PREFERRED_MODE_CHANGED, 0));
+ }
+ }
+
+ /**
* Receiver for misc intent broadcasts the Phone app cares about.
*/
private class PhoneAppBroadcastReceiver extends BroadcastReceiver {
@@ -1420,12 +1412,6 @@
mHandler.sendEmptyMessage(disconnectedDueToRoaming
? EVENT_DATA_ROAMING_DISCONNECTED
: EVENT_DATA_ROAMING_OK);
- } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
- if (VDBG) Log.d(LOG_TAG, "mReceiver: ACTION_HEADSET_PLUG");
- if (VDBG) Log.d(LOG_TAG, " state: " + intent.getIntExtra("state", 0));
- if (VDBG) Log.d(LOG_TAG, " name: " + intent.getStringExtra("name"));
- mIsHeadsetPlugged = (intent.getIntExtra("state", 0) == 1);
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_WIRED_HEADSET_PLUG, 0));
} else if ((action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) &&
(mPUKEntryActivity != null)) {
// if an attempt to un-PUK-lock the device was made, while we're
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index f243123..93f3002 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -2460,11 +2460,15 @@
boolean activated = false;
if (PhoneGlobals.mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
if (DBG) log("activateSpeakerIfDocked(): In a dock -> may need to turn on speaker.");
- PhoneGlobals app = PhoneGlobals.getInstance();
- final BluetoothManager btManager = app.getBluetoothManager();
+ final PhoneGlobals app = PhoneGlobals.getInstance();
- if (!app.isHeadsetPlugged() && !btManager.isBluetoothHeadsetAudioOn()) {
- turnOnSpeaker(phone.getContext(), true, true);
+ // TODO(klp): This function should move to AudioRouter
+ final BluetoothManager btManager = app.getBluetoothManager();
+ final WiredHeadsetManager wiredHeadset = app.getWiredHeadsetManager();
+ final AudioRouter audioRouter = app.getAudioRouter();
+
+ if (!wiredHeadset.isHeadsetPlugged() && !btManager.isBluetoothHeadsetAudioOn()) {
+ audioRouter.setSpeaker(true);
activated = true;
}
}
diff --git a/src/com/android/phone/WiredHeadsetManager.java b/src/com/android/phone/WiredHeadsetManager.java
new file mode 100644
index 0000000..4f09db6
--- /dev/null
+++ b/src/com/android/phone/WiredHeadsetManager.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone;
+
+import com.google.android.collect.Lists;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * Listens for and caches headset state. Used By the AudioRouter for maintaining
+ * overall audio state for use in the UI layer. Also provides method for connecting the bluetooth
+ * headset to the phone call.
+ */
+public class WiredHeadsetManager {
+ private static final String LOG_TAG = WiredHeadsetManager.class.getSimpleName();
+ private static final boolean DBG =
+ (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
+ private static final boolean VDBG = (PhoneGlobals.DBG_LEVEL >= 2);
+
+ // True if a wired headset is currently plugged in, based on the state
+ // from the latest Intent.ACTION_HEADSET_PLUG broadcast we received in
+ // mReceiver.onReceive().
+ private boolean mIsHeadsetPlugged = false;
+ private final WiredHeadsetBroadcastReceiver mReceiver;
+ private final List<WiredHeadsetListener> mListeners = Lists.newArrayList();
+
+ public WiredHeadsetManager(Context context) {
+ mReceiver = new WiredHeadsetBroadcastReceiver();
+
+ // Register for misc other intent broadcasts.
+ IntentFilter intentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
+ context.registerReceiver(mReceiver, intentFilter);
+ }
+
+ /**
+ * Returns connection state of the wires headset.
+ */
+ public boolean isHeadsetPlugged() {
+ return mIsHeadsetPlugged;
+ }
+
+ /**
+ * Add a listener for wired headset connection status.
+ */
+ public void addWiredHeadsetListener(WiredHeadsetListener listener) {
+ if (!mListeners.contains(listener)) {
+ mListeners.add(listener);
+ }
+ }
+
+ /**
+ * Called when we get an event from the system for the headset connection state.
+ */
+ private void onHeadsetConnection(boolean pluggedIn) {
+ if (DBG) {
+ Log.d(LOG_TAG, "Wired headset connected: " + pluggedIn);
+ }
+ mIsHeadsetPlugged = pluggedIn;
+
+ notifyListeners();
+ }
+
+ private void notifyListeners() {
+ for (int i = 0; i < mListeners.size(); i++) {
+ mListeners.get(i).onWiredHeadsetConnection(mIsHeadsetPlugged);
+ }
+ }
+
+ /**
+ * Receiver for misc intent broadcasts the BluetoothManager cares about.
+ */
+ private class WiredHeadsetBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
+ if (VDBG) Log.d(LOG_TAG, "mReceiver: ACTION_HEADSET_PLUG");
+ if (VDBG) Log.d(LOG_TAG, " state: " + intent.getIntExtra("state", 0));
+ if (VDBG) Log.d(LOG_TAG, " name: " + intent.getStringExtra("name"));
+ onHeadsetConnection(intent.getIntExtra("state", 0) == 1);
+ }
+ }
+ }
+
+ /**
+ * Listeners for those that want to know about the headset state.
+ */
+ public interface WiredHeadsetListener {
+ void onWiredHeadsetConnection(boolean pluggedIn);
+ }
+}