am 3294d44b: Add confcall management to SIP calls

Merge commit '3294d44b96f63f647fba3a03604eb028e28a42bc' into gingerbread-plus-aosp

* commit '3294d44b96f63f647fba3a03604eb028e28a42bc':
  Add confcall management to SIP calls
This commit is contained in:
Hung-ying Tyan
2010-08-24 05:00:11 -07:00
committed by Android Git Automerger
6 changed files with 142 additions and 94 deletions

View File

@@ -322,7 +322,9 @@ public final class CallManager {
} }
break; break;
} }
audioManager.setMode(mode); // calling audioManager.setMode() multiple times in a short period of
// time seems to break the audio recorder in in-call mode
if (audioManager.getMode() != mode) audioManager.setMode(mode);
} }
private Context getContext() { private Context getContext() {

View File

@@ -85,8 +85,10 @@ abstract class SipConnectionBase extends Connection {
protected void setState(Call.State state) { protected void setState(Call.State state) {
switch (state) { switch (state) {
case ACTIVE: case ACTIVE:
connectTimeReal = SystemClock.elapsedRealtime(); if (connectTime == 0) {
connectTime = System.currentTimeMillis(); connectTimeReal = SystemClock.elapsedRealtime();
connectTime = System.currentTimeMillis();
}
break; break;
case DISCONNECTED: case DISCONNECTED:
duration = SystemClock.elapsedRealtime() - connectTimeReal; duration = SystemClock.elapsedRealtime() - connectTimeReal;

View File

@@ -100,7 +100,7 @@ public class SipPhone extends SipPhoneBase {
} }
public String getPhoneName() { public String getPhoneName() {
return mProfile.getProfileName(); return "SIP:" + getUriString(mProfile);
} }
public String getSipUri() { public String getSipUri() {
@@ -222,21 +222,25 @@ public class SipPhone extends SipPhoneBase {
} }
public void conference() throws CallStateException { public void conference() throws CallStateException {
if ((foregroundCall.getState() != SipCall.State.ACTIVE) synchronized (SipPhone.class) {
|| (foregroundCall.getState() != SipCall.State.ACTIVE)) { if ((foregroundCall.getState() != SipCall.State.ACTIVE)
throw new CallStateException("wrong state to merge calls: fg=" || (foregroundCall.getState() != SipCall.State.ACTIVE)) {
+ foregroundCall.getState() + ", bg=" throw new CallStateException("wrong state to merge calls: fg="
+ backgroundCall.getState()); + foregroundCall.getState() + ", bg="
+ backgroundCall.getState());
}
foregroundCall.merge(backgroundCall);
} }
foregroundCall.merge(backgroundCall);
} }
public void conference(Call that) throws CallStateException { public void conference(Call that) throws CallStateException {
if (!(that instanceof SipCall)) { synchronized (SipPhone.class) {
throw new CallStateException("expect " + SipCall.class if (!(that instanceof SipCall)) {
+ ", cannot merge with " + that.getClass()); throw new CallStateException("expect " + SipCall.class
+ ", cannot merge with " + that.getClass());
}
foregroundCall.merge((SipCall) that);
} }
foregroundCall.merge((SipCall) that);
} }
public boolean canTransfer() { public boolean canTransfer() {
@@ -248,12 +252,14 @@ public class SipPhone extends SipPhoneBase {
} }
public void clearDisconnected() { public void clearDisconnected() {
ringingCall.clearDisconnected(); synchronized (SipPhone.class) {
foregroundCall.clearDisconnected(); ringingCall.clearDisconnected();
backgroundCall.clearDisconnected(); foregroundCall.clearDisconnected();
backgroundCall.clearDisconnected();
updatePhoneState(); updatePhoneState();
notifyPreciseCallStateChanged(); notifyPreciseCallStateChanged();
}
} }
public void sendDtmf(char c) { public void sendDtmf(char c) {
@@ -261,7 +267,9 @@ public class SipPhone extends SipPhoneBase {
Log.e(LOG_TAG, Log.e(LOG_TAG,
"sendDtmf called with invalid character '" + c + "'"); "sendDtmf called with invalid character '" + c + "'");
} else if (foregroundCall.getState().isAlive()) { } else if (foregroundCall.getState().isAlive()) {
foregroundCall.sendDtmf(c); synchronized (SipPhone.class) {
foregroundCall.sendDtmf(c);
}
} }
} }
@@ -307,7 +315,9 @@ public class SipPhone extends SipPhoneBase {
} }
public void setMute(boolean muted) { public void setMute(boolean muted) {
foregroundCall.setMute(muted); synchronized (SipPhone.class) {
foregroundCall.setMute(muted);
}
} }
public boolean getMute() { public boolean getMute() {
@@ -410,18 +420,20 @@ public class SipPhone extends SipPhoneBase {
@Override @Override
public void hangup() throws CallStateException { public void hangup() throws CallStateException {
Log.v(LOG_TAG, "hang up call: " + getState() + ": " + this synchronized (SipPhone.class) {
+ " on phone " + getPhone()); Log.v(LOG_TAG, "hang up call: " + getState() + ": " + this
CallStateException excp = null; + " on phone " + getPhone());
for (Connection c : connections) { CallStateException excp = null;
try { for (Connection c : connections) {
c.hangup(); try {
} catch (CallStateException e) { c.hangup();
excp = e; } catch (CallStateException e) {
excp = e;
}
} }
if (excp != null) throw excp;
setState(State.DISCONNECTING);
} }
if (excp != null) throw excp;
setState(State.DISCONNECTING);
} }
void initIncomingCall(SipAudioCall sipAudioCall, boolean makeCallWait) { void initIncomingCall(SipAudioCall sipAudioCall, boolean makeCallWait) {
@@ -454,19 +466,20 @@ public class SipPhone extends SipPhoneBase {
} }
void hold() throws CallStateException { void hold() throws CallStateException {
AudioGroup audioGroup = getAudioGroup();
if (audioGroup == null) return;
audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
setState(State.HOLDING); setState(State.HOLDING);
AudioGroup audioGroup = getAudioGroup();
if (audioGroup != null) {
audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
}
for (Connection c : connections) ((SipConnection) c).hold(); for (Connection c : connections) ((SipConnection) c).hold();
} }
void unhold() throws CallStateException { void unhold() throws CallStateException {
AudioGroup audioGroup = getAudioGroup();
if (audioGroup == null) return;
audioGroup.setMode(AudioGroup.MODE_NORMAL);
setState(State.ACTIVE); setState(State.ACTIVE);
for (Connection c : connections) ((SipConnection) c).unhold(); AudioGroup audioGroup = new AudioGroup();
for (Connection c : connections) {
((SipConnection) c).unhold(audioGroup);
}
} }
void setMute(boolean muted) { void setMute(boolean muted) {
@@ -483,17 +496,26 @@ public class SipPhone extends SipPhoneBase {
} }
void merge(SipCall that) throws CallStateException { void merge(SipCall that) throws CallStateException {
AudioGroup myGroup = getAudioGroup(); AudioGroup audioGroup = getAudioGroup();
for (Connection c : that.connections) { for (Connection c : that.connections) {
SipConnection conn = (SipConnection) c; SipConnection conn = (SipConnection) c;
conn.mergeTo(myGroup); add(conn);
connections.add(conn); if (conn.getState() == Call.State.HOLDING) {
conn.changeOwner(this); conn.unhold(audioGroup);
}
} }
that.connections.clear();
that.setState(Call.State.IDLE); that.setState(Call.State.IDLE);
} }
private void add(SipConnection conn) {
SipCall call = conn.getCall();
if (call == this) return;
if (call != null) call.connections.remove(conn);
connections.add(conn);
conn.changeOwner(this);
}
void sendDtmf(char c) { void sendDtmf(char c) {
AudioGroup audioGroup = getAudioGroup(); AudioGroup audioGroup = getAudioGroup();
if (audioGroup == null) return; if (audioGroup == null) return;
@@ -568,7 +590,6 @@ public class SipPhone extends SipPhoneBase {
private class SipConnection extends SipConnectionBase { private class SipConnection extends SipConnectionBase {
private SipCall mOwner; private SipCall mOwner;
private SipAudioCall mSipAudioCall; private SipAudioCall mSipAudioCall;
private AudioGroup mOriginalGroup;
private Call.State mState = Call.State.IDLE; private Call.State mState = Call.State.IDLE;
private SipProfile mPeer; private SipProfile mPeer;
private boolean mIncoming = false; private boolean mIncoming = false;
@@ -673,6 +694,7 @@ public class SipPhone extends SipPhoneBase {
} }
void hold() throws CallStateException { void hold() throws CallStateException {
setState(Call.State.HOLDING);
try { try {
mSipAudioCall.holdCall(); mSipAudioCall.holdCall();
} catch (SipException e) { } catch (SipException e) {
@@ -680,7 +702,9 @@ public class SipPhone extends SipPhoneBase {
} }
} }
void unhold() throws CallStateException { void unhold(AudioGroup audioGroup) throws CallStateException {
mSipAudioCall.setAudioGroup(audioGroup);
setState(Call.State.ACTIVE);
try { try {
mSipAudioCall.continueCall(); mSipAudioCall.continueCall();
} catch (SipException e) { } catch (SipException e) {
@@ -688,16 +712,6 @@ public class SipPhone extends SipPhoneBase {
} }
} }
void mergeTo(AudioGroup group) throws CallStateException {
AudioStream stream = mSipAudioCall.getAudioStream();
if (stream == null) {
throw new CallStateException("wrong state to merge: "
+ mSipAudioCall.getState());
}
if (mOriginalGroup == null) mOriginalGroup = getAudioGroup();
stream.join(group);
}
@Override @Override
protected void setState(Call.State state) { protected void setState(Call.State state) {
if (state == mState) return; if (state == mState) return;
@@ -732,29 +746,36 @@ public class SipPhone extends SipPhoneBase {
@Override @Override
public void hangup() throws CallStateException { public void hangup() throws CallStateException {
// TODO: need to pull AudioStream out of the AudioGroup in case synchronized (SipPhone.class) {
// this conn was part of a conf call Log.v(LOG_TAG, "hangup conn: " + mPeer.getUriString() + ": "
Log.v(LOG_TAG, "hangup conn: " + mPeer.getUriString() + ": " + ": on phone " + getPhone().getPhoneName());
+ ": on phone " + getPhone()); try {
try { mSipAudioCall.endCall();
mSipAudioCall.endCall(); setState(Call.State.DISCONNECTING);
setState(Call.State.DISCONNECTING); setDisconnectCause(DisconnectCause.LOCAL);
setDisconnectCause(DisconnectCause.LOCAL); } catch (SipException e) {
} catch (SipException e) { throw new CallStateException("hangup(): " + e);
throw new CallStateException("hangup(): " + e); }
} }
} }
@Override @Override
public void separate() throws CallStateException { public void separate() throws CallStateException {
// TODO: what's this for SIP? synchronized (SipPhone.class) {
/* SipCall call = (SipCall) SipPhone.this.getBackgroundCall();
if (!disconnected) { if (call.getState() != Call.State.IDLE) {
owner.separate(this); throw new CallStateException(
} else { "cannot put conn back to a call in non-idle state: "
throw new CallStateException ("disconnected"); + call.getState());
}
Log.v(LOG_TAG, "separate conn: " + mPeer.getUriString()
+ " from " + mOwner + " back to " + call);
AudioGroup audioGroup = call.getAudioGroup(); // may be null
call.add(this);
mSipAudioCall.setAudioGroup(audioGroup);
call.hold();
} }
*/
} }
@Override @Override

View File

@@ -175,10 +175,6 @@ abstract class SipPhoneBase extends PhoneBase {
return state; return state;
} }
public String getPhoneName() {
return "SIP";
}
public int getPhoneType() { public int getPhoneType() {
// FIXME: add SIP phone type // FIXME: add SIP phone type
return Phone.PHONE_TYPE_GSM; return Phone.PHONE_TYPE_GSM;

View File

@@ -244,7 +244,8 @@ public interface SipAudioCall {
* Also, the {@code AudioStream} may change its group during a call (e.g., * Also, the {@code AudioStream} may change its group during a call (e.g.,
* after the call is held/un-held). Finally, the {@code AudioGroup} object * after the call is held/un-held). Finally, the {@code AudioGroup} object
* returned by this method is undefined after the call ends or the * returned by this method is undefined after the call ends or the
* {@link #close} method is called. * {@link #close} method is called. If a group object is set by
* {@link #setAudioGroup(AudioGroup)}, then this method returns that object.
* *
* @return the {@link AudioGroup} object or null if the RTP stream has not * @return the {@link AudioGroup} object or null if the RTP stream has not
* yet been set up * yet been set up
@@ -252,6 +253,15 @@ public interface SipAudioCall {
*/ */
AudioGroup getAudioGroup(); AudioGroup getAudioGroup();
/**
* Sets the {@link AudioGroup} object which the {@link AudioStream} object
* joins. If {@code audioGroup} is null, then the {@code AudioGroup} object
* will be dynamically created when needed.
*
* @see #getAudioStream
*/
void setAudioGroup(AudioGroup audioGroup);
/** /**
* Checks if the call is established. * Checks if the call is established.
* *

View File

@@ -70,7 +70,8 @@ public class SipAudioCallImpl extends SipSessionAdapter
private ISipSession mSipSession; private ISipSession mSipSession;
private SdpSessionDescription mPeerSd; private SdpSessionDescription mPeerSd;
private AudioStream mRtpSession; private AudioStream mAudioStream;
private AudioGroup mAudioGroup;
private SdpSessionDescription.AudioCodec mCodec; private SdpSessionDescription.AudioCodec mCodec;
private long mSessionId = -1L; // SDP session ID private long mSessionId = -1L; // SDP session ID
private boolean mInCall = false; private boolean mInCall = false;
@@ -505,11 +506,19 @@ public class SipAudioCallImpl extends SipSessionAdapter
} }
public synchronized AudioStream getAudioStream() { public synchronized AudioStream getAudioStream() {
return mRtpSession; return mAudioStream;
} }
public synchronized AudioGroup getAudioGroup() { public synchronized AudioGroup getAudioGroup() {
return ((mRtpSession == null) ? null : mRtpSession.getAudioGroup()); if (mAudioGroup != null) return mAudioGroup;
return ((mAudioStream == null) ? null : mAudioStream.getAudioGroup());
}
public synchronized void setAudioGroup(AudioGroup group) {
if ((mAudioStream != null) && (mAudioStream.getAudioGroup() != null)) {
mAudioStream.join(group);
}
mAudioGroup = group;
} }
private SdpSessionDescription.AudioCodec getCodec(SdpSessionDescription sd) { private SdpSessionDescription.AudioCodec getCodec(SdpSessionDescription sd) {
@@ -561,7 +570,7 @@ public class SipAudioCallImpl extends SipSessionAdapter
// TODO: get sample rate from sdp // TODO: get sample rate from sdp
mCodec = getCodec(peerSd); mCodec = getCodec(peerSd);
AudioStream audioStream = mRtpSession; AudioStream audioStream = mAudioStream;
audioStream.associate(InetAddress.getByName(peerMediaAddress), audioStream.associate(InetAddress.getByName(peerMediaAddress),
peerMediaPort); peerMediaPort);
audioStream.setCodec(convert(mCodec), mCodec.payloadType); audioStream.setCodec(convert(mCodec), mCodec.payloadType);
@@ -580,7 +589,7 @@ public class SipAudioCallImpl extends SipSessionAdapter
Log.d(TAG, " not sending"); Log.d(TAG, " not sending");
audioStream.setMode(RtpStream.MODE_RECEIVE_ONLY); audioStream.setMode(RtpStream.MODE_RECEIVE_ONLY);
} }
} else {
/* The recorder volume will be very low if the device is in /* The recorder volume will be very low if the device is in
* IN_CALL mode. Therefore, we have to set the mode to NORMAL * IN_CALL mode. Therefore, we have to set the mode to NORMAL
* in order to have the normal microphone level. * in order to have the normal microphone level.
@@ -590,14 +599,22 @@ public class SipAudioCallImpl extends SipSessionAdapter
.setMode(AudioManager.MODE_NORMAL); .setMode(AudioManager.MODE_NORMAL);
} }
AudioGroup audioGroup = new AudioGroup(); // AudioGroup logic:
audioStream.join(audioGroup); AudioGroup audioGroup = getAudioGroup();
if (mHold) { if (mHold) {
audioGroup.setMode(AudioGroup.MODE_ON_HOLD); if (audioGroup != null) {
} else if (mMuted) { audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
audioGroup.setMode(AudioGroup.MODE_MUTED); }
// don't create an AudioGroup here; doing so will fail if
// there's another AudioGroup out there that's active
} else { } else {
audioGroup.setMode(AudioGroup.MODE_NORMAL); if (audioGroup == null) audioGroup = new AudioGroup();
audioStream.join(audioGroup);
if (mMuted) {
audioGroup.setMode(AudioGroup.MODE_MUTED);
} else {
audioGroup.setMode(AudioGroup.MODE_NORMAL);
}
} }
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "call()", e); Log.e(TAG, "call()", e);
@@ -606,20 +623,20 @@ public class SipAudioCallImpl extends SipSessionAdapter
private void stopCall(boolean releaseSocket) { private void stopCall(boolean releaseSocket) {
Log.d(TAG, "stop audiocall"); Log.d(TAG, "stop audiocall");
if (mRtpSession != null) { if (mAudioStream != null) {
mRtpSession.join(null); mAudioStream.join(null);
if (releaseSocket) { if (releaseSocket) {
mRtpSession.release(); mAudioStream.release();
mRtpSession = null; mAudioStream = null;
} }
} }
} }
private int getLocalMediaPort() { private int getLocalMediaPort() {
if (mRtpSession != null) return mRtpSession.getLocalPort(); if (mAudioStream != null) return mAudioStream.getLocalPort();
try { try {
AudioStream s = mRtpSession = AudioStream s = mAudioStream =
new AudioStream(InetAddress.getByName(getLocalIp())); new AudioStream(InetAddress.getByName(getLocalIp()));
return s.getLocalPort(); return s.getLocalPort();
} catch (IOException e) { } catch (IOException e) {