SipPhone.java revision e86a8d443e921caec2c398ef74f0b6d573a15bb1
10825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/* 20825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Copyright (C) 2010 The Android Open Source Project 30825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 40825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Licensed under the Apache License, Version 2.0 (the "License"); 50825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * you may not use this file except in compliance with the License. 60825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * You may obtain a copy of the License at 70825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 80825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * http://www.apache.org/licenses/LICENSE-2.0 90825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Unless required by applicable law or agreed to in writing, software 110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * distributed under the License is distributed on an "AS IS" BASIS, 120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * See the License for the specific language governing permissions and 140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * limitations under the License. 150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 170825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillepackage com.android.internal.telephony.sip; 180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 190825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.content.Context; 200825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.media.AudioManager; 210825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.net.rtp.AudioGroup; 220825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.net.sip.SipAudioCall; 230825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.net.sip.SipErrorCode; 240825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.net.sip.SipException; 250825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.net.sip.SipManager; 260825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.net.sip.SipProfile; 270825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.net.sip.SipSession; 280825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.os.AsyncResult; 290825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.os.Message; 30b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensenimport android.telephony.DisconnectCause; 310825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.telephony.PhoneNumberUtils; 320825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.telephony.ServiceState; 330825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.text.TextUtils; 3499c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Savilleimport android.telephony.Rlog; 350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 360825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.Call; 370825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.CallStateException; 380825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.Connection; 390825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.Phone; 400825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.PhoneConstants; 410825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.PhoneNotifier; 420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 430825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.text.ParseException; 440825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.List; 450825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.regex.Pattern; 460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/** 480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * {@hide} 490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 500825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillepublic class SipPhone extends SipPhoneBase { 510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String LOG_TAG = "SipPhone"; 52f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final boolean DBG = true; 53ff4e317d24f0d23bdc0f306d53ddc51f2f1ecf6aWink Saville private static final boolean VDBG = false; // STOPSHIP if true 540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int TIMEOUT_MAKE_CALL = 15; // in seconds 550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int TIMEOUT_ANSWER_CALL = 8; // in seconds 560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int TIMEOUT_HOLD_CALL = 15; // in seconds 570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // A call that is ringing or (call) waiting 5922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville private SipCall mRingingCall = new SipCall(); 6022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville private SipCall mForegroundCall = new SipCall(); 6122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville private SipCall mBackgroundCall = new SipCall(); 620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipManager mSipManager; 640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipProfile mProfile; 650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipPhone (Context context, PhoneNotifier notifier, SipProfile profile) { 67ff4e317d24f0d23bdc0f306d53ddc51f2f1ecf6aWink Saville super("SIP:" + profile.getUriString(), context, notifier); 680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 69f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("new SipPhone: " + profile.getUriString()); 7022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall = new SipCall(); 7122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall = new SipCall(); 7222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mBackgroundCall = new SipCall(); 730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mProfile = profile; 740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipManager = SipManager.newInstance(context); 750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean equals(Object o) { 790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (o == this) return true; 800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!(o instanceof SipPhone)) return false; 810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipPhone that = (SipPhone) o; 820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mProfile.getUriString().equals(that.mProfile.getUriString()); 830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public String getSipUri() { 860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mProfile.getUriString(); 870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean equals(SipPhone phone) { 900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return getSipUri().equals(phone.getSipUri()); 910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 93e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal public Connection takeIncomingCall(Object incomingCall) { 94f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // FIXME: Is synchronizing on the class necessary, should we use a mLockObj? 95f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // Also there are many things not synchronized, of course 96f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // this may be true of CdmaPhone and GsmPhone too!!! 970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 98f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (!(incomingCall instanceof SipAudioCall)) { 99e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (DBG) log("takeIncomingCall: ret=null, not a SipAudioCall"); 100e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal return null; 101f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 10222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mRingingCall.getState().isAlive()) { 103e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (DBG) log("takeIncomingCall: ret=null, ringingCall not alive"); 104e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal return null; 105f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 1060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: is it true that we cannot take any incoming call if 1080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // both foreground and background are active 10922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mForegroundCall.getState().isAlive() 11022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville && mBackgroundCall.getState().isAlive()) { 111f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) { 112e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal log("takeIncomingCall: ret=null," + " foreground and background both alive"); 113f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 114e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal return null; 1150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 1180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipAudioCall sipAudioCall = (SipAudioCall) incomingCall; 119e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (DBG) log("takeIncomingCall: taking call from: " 1200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + sipAudioCall.getPeerProfile().getUriString()); 1210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String localUri = sipAudioCall.getLocalProfile().getUriString(); 1220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (localUri.equals(mProfile.getUriString())) { 12322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville boolean makeCallWait = mForegroundCall.getState().isAlive(); 124e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal SipConnection connection = mRingingCall.initIncomingCall(sipAudioCall, 125e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal makeCallWait); 126e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (sipAudioCall.getState() != SipSession.State.INCOMING_CALL) { 1270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Peer cancelled the call! 128e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (DBG) log(" takeIncomingCall: call cancelled !!"); 12922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.reset(); 130e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal connection = null; 1310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 132e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal return connection; 1330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (Exception e) { 1350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Peer may cancel the call at any time during the time we hook 1360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // up ringingCall with sipAudioCall. Clean up ringingCall when 1370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // that happens. 138e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (DBG) log(" takeIncomingCall: exception e=" + e); 13922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.reset(); 1400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 141e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (DBG) log("takeIncomingCall: NOT taking !!"); 142e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal return null; 1430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 146f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 1470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void acceptCall() throws CallStateException { 1480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 14922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if ((mRingingCall.getState() == Call.State.INCOMING) || 15022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville (mRingingCall.getState() == Call.State.WAITING)) { 151f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("acceptCall: accepting"); 1520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Always unmute when answering a new call 15322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.setMute(false); 15422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.acceptCall(); 1550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 156f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) { 157f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville log("acceptCall:" + 158f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville " throw CallStateException(\"phone not ringing\")"); 159f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 1600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("phone not ringing"); 1610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 165f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 1660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void rejectCall() throws CallStateException { 1670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 16822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mRingingCall.getState().isRinging()) { 169f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("rejectCall: rejecting"); 17022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.rejectCall(); 1710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 172f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) { 173f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville log("rejectCall:" + 174f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville " throw CallStateException(\"phone not ringing\")"); 175f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 1760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("phone not ringing"); 1770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 181f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 1826bbcbfd62c9aa5787e7c33936e2246ff05b59d58Tyler Gunn public Connection dial(String dialString, int videoState) throws CallStateException { 1830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 1846bbcbfd62c9aa5787e7c33936e2246ff05b59d58Tyler Gunn return dialInternal(dialString, videoState); 1850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1886bbcbfd62c9aa5787e7c33936e2246ff05b59d58Tyler Gunn private Connection dialInternal(String dialString, int videoState) 1890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throws CallStateException { 190f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("dialInternal: dialString=" + (VDBG ? dialString : "xxxxxx")); 1910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville clearDisconnected(); 1920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!canDial()) { 194f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville throw new CallStateException("dialInternal: cannot dial in current state"); 1950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 19622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mForegroundCall.getState() == SipCall.State.ACTIVE) { 1970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switchHoldingAndActive(); 1980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 19922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mForegroundCall.getState() != SipCall.State.IDLE) { 2000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville //we should have failed in !canDial() above before we get here 2010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("cannot dial in current state"); 2020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 20422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.setMute(false); 2050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 20622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville Connection c = mForegroundCall.dial(dialString); 2070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return c; 2080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 209f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville loge("dialInternal: ", e); 2100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("dial error: " + e); 2110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 214f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void switchHoldingAndActive() throws CallStateException { 216f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("dialInternal: switch fg and bg"); 2170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 21822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.switchWith(mBackgroundCall); 21922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mBackgroundCall.getState().isAlive()) mBackgroundCall.hold(); 22022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mForegroundCall.getState().isAlive()) mForegroundCall.unhold(); 2210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 224f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean canConference() { 226f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("canConference: ret=true"); 2270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return true; 2280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 230f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void conference() throws CallStateException { 2320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 23322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if ((mForegroundCall.getState() != SipCall.State.ACTIVE) 23422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville || (mForegroundCall.getState() != SipCall.State.ACTIVE)) { 2350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("wrong state to merge calls: fg=" 23622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville + mForegroundCall.getState() + ", bg=" 23722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville + mBackgroundCall.getState()); 2380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 239f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("conference: merge fg & bg"); 24022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.merge(mBackgroundCall); 2410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void conference(Call that) throws CallStateException { 2450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 2460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!(that instanceof SipCall)) { 2470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("expect " + SipCall.class 2480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ", cannot merge with " + that.getClass()); 2490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 25022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.merge((SipCall) that); 2510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 254f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean canTransfer() { 2560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return false; 2570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 259f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 260cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville public void explicitCallTransfer() { 2610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville //mCT.explicitCallTransfer(); 2620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 264f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void clearDisconnected() { 2660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 26722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.clearDisconnected(); 26822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.clearDisconnected(); 26922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mBackgroundCall.clearDisconnected(); 2700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville updatePhoneState(); 2720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville notifyPreciseCallStateChanged(); 2730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 276f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void sendDtmf(char c) { 2780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!PhoneNumberUtils.is12Key(c)) { 279f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville loge("sendDtmf called with invalid character '" + c + "'"); 28022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville } else if (mForegroundCall.getState().isAlive()) { 2810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 28222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.sendDtmf(c); 2830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 287f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void startDtmf(char c) { 2890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!PhoneNumberUtils.is12Key(c)) { 290f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville loge("startDtmf called with invalid character '" + c + "'"); 2910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 2920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sendDtmf(c); 2930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 296f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void stopDtmf() { 2980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // no op 2990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void sendBurstDtmf(String dtmfString) { 302f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville loge("sendBurstDtmf() is a CDMA method"); 3030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 305f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void getOutgoingCallerIdDisplay(Message onComplete) { 3070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: what to reply? 3080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AsyncResult.forMessage(onComplete, null, null); 3090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onComplete.sendToTarget(); 3100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 312f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, 3140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Message onComplete) { 3150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: what's this for SIP? 3160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AsyncResult.forMessage(onComplete, null, null); 3170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onComplete.sendToTarget(); 3180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 320f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void getCallWaiting(Message onComplete) { 3220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: what to reply? 3230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AsyncResult.forMessage(onComplete, null, null); 3240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onComplete.sendToTarget(); 3250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 327f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void setCallWaiting(boolean enable, Message onComplete) { 3290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: what to reply? 330f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville loge("call waiting not supported"); 3310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 334184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota public void setEchoSuppressionEnabled() { 335184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota // Echo suppression may not be available on every device. So, check 336184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota // whether it is supported 3370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 338184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 339184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota String echoSuppression = audioManager.getParameters("ec_supported"); 340184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota if (echoSuppression.contains("off")) { 341184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota mForegroundCall.setAudioGroupMode(); 342184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota } 3430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 346f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void setMute(boolean muted) { 3480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 34922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.setMute(muted); 3500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 353f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean getMute() { 35522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return (mForegroundCall.getState().isAlive() 35622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville ? mForegroundCall.getMute() 35722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville : mBackgroundCall.getMute()); 3580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 360f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Call getForegroundCall() { 36222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return mForegroundCall; 3630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 365f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Call getBackgroundCall() { 36722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return mBackgroundCall; 3680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 370f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Call getRingingCall() { 37222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return mRingingCall; 3730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 375f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public ServiceState getServiceState() { 3770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: we may need to provide this when data connectivity is lost 3780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // or when server is down 3790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return super.getServiceState(); 3800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private String getUriString(SipProfile p) { 3830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // SipProfile.getUriString() may contain "SIP:" and port 3840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return p.getUserName() + "@" + getSipDomain(p); 3850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private String getSipDomain(SipProfile p) { 3880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String domain = p.getSipDomain(); 3890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // TODO: move this to SipProfile 3900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (domain.endsWith(":5060")) { 3910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return domain.substring(0, domain.length() - 5); 3920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 3930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return domain; 3940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 397f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static Call.State getCallStateFrom(SipAudioCall sipAudioCall) { 398f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (sipAudioCall.isOnHold()) return Call.State.HOLDING; 399f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville int sessionState = sipAudioCall.getState(); 400f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville switch (sessionState) { 401f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.READY_TO_CALL: return Call.State.IDLE; 402f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.INCOMING_CALL: 403f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.INCOMING_CALL_ANSWERING: return Call.State.INCOMING; 404f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.OUTGOING_CALL: return Call.State.DIALING; 405f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.OUTGOING_CALL_RING_BACK: return Call.State.ALERTING; 406f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.OUTGOING_CALL_CANCELING: return Call.State.DISCONNECTING; 407f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.IN_CALL: return Call.State.ACTIVE; 408f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville default: 409f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville slog("illegal connection state: " + sessionState); 410f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return Call.State.DISCONNECTED; 411f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 412f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 413f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 414f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void log(String s) { 415f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.d(LOG_TAG, s); 416f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 417f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 418f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static void slog(String s) { 419f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.d(LOG_TAG, s); 420f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 421f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 422f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void loge(String s) { 423f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.e(LOG_TAG, s); 424f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 425f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 426f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void loge(String s, Exception e) { 427f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.e(LOG_TAG, s, e); 428f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 429f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 4300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private class SipCall extends SipCallBase { 431f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final String SC_TAG = "SipCall"; 432f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final boolean SC_DBG = true; 433f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final boolean SC_VDBG = false; // STOPSHIP if true 434f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 4350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void reset() { 436f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("reset"); 43722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mConnections.clear(); 4380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.IDLE); 4390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void switchWith(SipCall that) { 442f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("switchWith"); 4430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 4440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipCall tmp = new SipCall(); 4450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville tmp.takeOver(this); 4460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville this.takeOver(that); 4470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville that.takeOver(tmp); 4480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void takeOver(SipCall that) { 452f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("takeOver"); 45322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mConnections = that.mConnections; 45422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mState = that.mState; 45522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) { 4560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ((SipConnection) c).changeOwner(this); 4570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 4610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Phone getPhone() { 4620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return SipPhone.this; 4630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 4660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public List<Connection> getConnections() { 467f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_VDBG) log("getConnections"); 4680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 4690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME should return Collections.unmodifiableList(); 47022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return mConnections; 4710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Connection dial(String originalNumber) throws SipException { 475f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("dial: num=" + (SC_VDBG ? originalNumber : "xxx")); 476f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // TODO: Should this be synchronized? 4770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String calleeSipUri = originalNumber; 4780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!calleeSipUri.contains("@")) { 4790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String replaceStr = Pattern.quote(mProfile.getUserName() + "@"); 4800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville calleeSipUri = mProfile.getUriString().replaceFirst(replaceStr, 4810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville calleeSipUri + "@"); 4820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 4840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipProfile callee = 4850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville new SipProfile.Builder(calleeSipUri).build(); 4860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipConnection c = new SipConnection(this, callee, 4870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville originalNumber); 4880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville c.dial(); 48922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mConnections.add(c); 4900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.DIALING); 4910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return c; 4920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (ParseException e) { 4930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new SipException("dial", e); 4940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 4980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void hangup() throws CallStateException { 4990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 50022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mState.isAlive()) { 501f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("hangup: call " + getState() 5020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ": " + this + " on phone " + getPhone()); 5030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(State.DISCONNECTING); 5040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville CallStateException excp = null; 50522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) { 5060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 5070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville c.hangup(); 5080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (CallStateException e) { 5090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville excp = e; 5100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (excp != null) throw excp; 5130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 514f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("hangup: dead call " + getState() 5150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ": " + this + " on phone " + getPhone()); 5160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 520e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal SipConnection initIncomingCall(SipAudioCall sipAudioCall, boolean makeCallWait) { 5210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipProfile callee = sipAudioCall.getPeerProfile(); 5220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipConnection c = new SipConnection(this, callee); 52322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mConnections.add(c); 5240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Call.State newState = makeCallWait ? State.WAITING : State.INCOMING; 5260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville c.initIncomingCall(sipAudioCall, newState); 5270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(newState); 5290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville notifyNewRingingConnectionP(c); 530e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal return c; 5310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void rejectCall() throws CallStateException { 534f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("rejectCall:"); 5350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville hangup(); 5360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void acceptCall() throws CallStateException { 539f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("acceptCall: accepting"); 54022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (this != mRingingCall) { 5410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("acceptCall() in a non-ringing call"); 5420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 54322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mConnections.size() != 1) { 5440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("acceptCall() in a conf call"); 5450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 54622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville ((SipConnection) mConnections.get(0)).acceptCall(); 5470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private boolean isSpeakerOn() { 550f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Boolean ret = ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)) 5510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville .isSpeakerphoneOn(); 552f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_VDBG) log("isSpeakerOn: ret=" + ret); 553f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return ret; 5540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void setAudioGroupMode() { 5570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = getAudioGroup(); 558f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (audioGroup == null) { 559f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("setAudioGroupMode: audioGroup == null ignore"); 560f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return; 561f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 5620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int mode = audioGroup.getMode(); 56322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mState == State.HOLDING) { 5640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.setMode(AudioGroup.MODE_ON_HOLD); 5650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else if (getMute()) { 5660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.setMode(AudioGroup.MODE_MUTED); 5670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else if (isSpeakerOn()) { 5680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.setMode(AudioGroup.MODE_ECHO_SUPPRESSION); 5690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 5700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.setMode(AudioGroup.MODE_NORMAL); 5710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 572f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log(String.format( 573f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville "setAudioGroupMode change: %d --> %d", mode, 5740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.getMode())); 5750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void hold() throws CallStateException { 578f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("hold:"); 5790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(State.HOLDING); 58022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) ((SipConnection) c).hold(); 5810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setAudioGroupMode(); 5820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void unhold() throws CallStateException { 585f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("unhold:"); 5860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(State.ACTIVE); 5870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = new AudioGroup(); 58822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) { 5890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ((SipConnection) c).unhold(audioGroup); 5900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setAudioGroupMode(); 5920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void setMute(boolean muted) { 595f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("setMute: muted=" + muted); 59622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) { 5970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ((SipConnection) c).setMute(muted); 5980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean getMute() { 60222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville boolean ret = mConnections.isEmpty() 6030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ? false 60422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville : ((SipConnection) mConnections.get(0)).getMute(); 605f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("getMute: ret=" + ret); 606f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return ret; 6070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void merge(SipCall that) throws CallStateException { 610f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("merge:"); 6110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = getAudioGroup(); 6120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // copy to an array to avoid concurrent modification as connections 6140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // in that.connections will be removed in add(SipConnection). 61522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville Connection[] cc = that.mConnections.toArray( 61622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville new Connection[that.mConnections.size()]); 6170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (Connection c : cc) { 6180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipConnection conn = (SipConnection) c; 6190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville add(conn); 6200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (conn.getState() == Call.State.HOLDING) { 6210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville conn.unhold(audioGroup); 6220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville that.setState(Call.State.IDLE); 6250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void add(SipConnection conn) { 628f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("add:"); 6290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipCall call = conn.getCall(); 6300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (call == this) return; 63122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (call != null) call.mConnections.remove(conn); 6320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 63322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mConnections.add(conn); 6340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville conn.changeOwner(this); 6350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void sendDtmf(char c) { 638f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("sendDtmf: c=" + c); 6390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = getAudioGroup(); 640f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (audioGroup == null) { 641f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("sendDtmf: audioGroup == null, ignore c=" + c); 642f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return; 643f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 6440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.sendDtmf(convertDtmf(c)); 6450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private int convertDtmf(char c) { 6480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int code = c - '0'; 6490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if ((code < 0) || (code > 9)) { 6500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch (c) { 6510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case '*': return 10; 6520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case '#': return 11; 6530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 'A': return 12; 6540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 'B': return 13; 6550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 'C': return 14; 6560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 'D': return 15; 6570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville default: 6580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new IllegalArgumentException( 6590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville "invalid DTMF char: " + (int) c); 6600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return code; 6630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 6660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected void setState(State newState) { 66722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mState != newState) { 66822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (SC_DBG) log("setState: cur state" + mState 6690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + " --> " + newState + ": " + this + ": on phone " 67022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville + getPhone() + " " + mConnections.size()); 6710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (newState == Call.State.ALERTING) { 67322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mState = newState; // need in ALERTING to enable ringback 67422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville startRingbackTone(); 67522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville } else if (mState == Call.State.ALERTING) { 67622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville stopRingbackTone(); 6770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 67822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mState = newState; 6790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville updatePhoneState(); 6800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville notifyPreciseCallStateChanged(); 6810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void onConnectionStateChanged(SipConnection conn) { 6850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // this can be called back when a conf call is formed 686f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("onConnectionStateChanged: conn=" + conn); 68722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mState != State.ACTIVE) { 6880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(conn.getState()); 6890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void onConnectionEnded(SipConnection conn) { 6930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // set state to DISCONNECTED only when all conns are disconnected 694f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("onConnectionEnded: conn=" + conn); 69522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mState != State.DISCONNECTED) { 6960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean allConnectionsDisconnected = true; 697f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("---check connections: " 69822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville + mConnections.size()); 69922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) { 700f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log(" state=" + c.getState() + ": " 7010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + c); 7020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (c.getState() != State.DISCONNECTED) { 7030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville allConnectionsDisconnected = false; 7040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 7050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (allConnectionsDisconnected) setState(State.DISCONNECTED); 7080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville notifyDisconnectP(conn); 7100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private AudioGroup getAudioGroup() { 71322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mConnections.isEmpty()) return null; 71422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return ((SipConnection) mConnections.get(0)).getAudioGroup(); 7150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 716f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 717f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void log(String s) { 718f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.d(SC_TAG, s); 719f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 7200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private class SipConnection extends SipConnectionBase { 723f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final String SCN_TAG = "SipConnection"; 724f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final boolean SCN_DBG = true; 725f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 7260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipCall mOwner; 7270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipAudioCall mSipAudioCall; 7280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private Call.State mState = Call.State.IDLE; 7290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipProfile mPeer; 7300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private boolean mIncoming = false; 731f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private String mOriginalNumber; // may be a PSTN number 7320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipAudioCallAdapter mAdapter = new SipAudioCallAdapter() { 7340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 735b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen protected void onCallEnded(int cause) { 7360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (getDisconnectCause() != DisconnectCause.LOCAL) { 7370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setDisconnectCause(cause); 7380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 7400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.DISCONNECTED); 7410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipAudioCall sipAudioCall = mSipAudioCall; 742f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // FIXME: This goes null and is synchronized, but many uses aren't sync'd 7430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall = null; 7440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String sessionState = (sipAudioCall == null) 7450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ? "" 7460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : (sipAudioCall.getState() + ", "); 747f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("[SipAudioCallAdapter] onCallEnded: " 7480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + mPeer.getUriString() + ": " + sessionState 7490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + "cause: " + getDisconnectCause() + ", on phone " 7500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + getPhone()); 7510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (sipAudioCall != null) { 7520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.setListener(null); 7530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.close(); 7540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOwner.onConnectionEnded(SipConnection.this); 7560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 7600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onCallEstablished(SipAudioCall call) { 7610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onChanged(call); 762f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // Race onChanged synchronized this isn't 7630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mState == Call.State.ACTIVE) call.startAudio(); 7640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 7670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onCallHeld(SipAudioCall call) { 7680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onChanged(call); 769f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // Race onChanged synchronized this isn't 7700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mState == Call.State.HOLDING) call.startAudio(); 7710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 7740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onChanged(SipAudioCall call) { 7750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 7760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Call.State newState = getCallStateFrom(call); 7770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mState == newState) return; 7780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (newState == Call.State.INCOMING) { 7790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(mOwner.getState()); // INCOMING or WAITING 7800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 78122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mOwner == mRingingCall) { 78222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mRingingCall.getState() == Call.State.WAITING) { 7830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 7840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switchHoldingAndActive(); 7850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (CallStateException e) { 7860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // disconnect the call. 7870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onCallEnded(DisconnectCause.LOCAL); 7880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return; 7890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 79122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.switchWith(mRingingCall); 7920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(newState); 7940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOwner.onConnectionStateChanged(SipConnection.this); 796f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("onChanged: " 7970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + mPeer.getUriString() + ": " + mState 7980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + " on phone " + getPhone()); 7990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 803b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen protected void onError(int cause) { 804f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("onError: " + cause); 8050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onCallEnded(cause); 8060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville }; 8080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public SipConnection(SipCall owner, SipProfile callee, 8100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String originalNumber) { 8110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville super(originalNumber); 8120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOwner = owner; 8130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mPeer = callee; 8140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOriginalNumber = originalNumber; 8150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public SipConnection(SipCall owner, SipProfile callee) { 8180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville this(owner, callee, getUriString(callee)); 8190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 8220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public String getCnapName() { 8230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String displayName = mPeer.getDisplayName(); 8240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return TextUtils.isEmpty(displayName) ? null 8250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : displayName; 8260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 8290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public int getNumberPresentation() { 8300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return PhoneConstants.PRESENTATION_ALLOWED; 8310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void initIncomingCall(SipAudioCall sipAudioCall, Call.State newState) { 8340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(newState); 8350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall = sipAudioCall; 8360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.setListener(mAdapter); // call back to set state 8370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mIncoming = true; 8380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void acceptCall() throws CallStateException { 8410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 8420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.answerCall(TIMEOUT_ANSWER_CALL); 8430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 8440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("acceptCall(): " + e); 8450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void changeOwner(SipCall owner) { 8490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOwner = owner; 8500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup getAudioGroup() { 8530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mSipAudioCall == null) return null; 8540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mSipAudioCall.getAudioGroup(); 8550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void dial() throws SipException { 8580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.DIALING); 8590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall = mSipManager.makeAudioCall(mProfile, mPeer, null, 8600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville TIMEOUT_MAKE_CALL); 8610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.setListener(mAdapter); 8620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void hold() throws CallStateException { 8650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.HOLDING); 8660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 8670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.holdCall(TIMEOUT_HOLD_CALL); 8680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 8690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("hold(): " + e); 8700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void unhold(AudioGroup audioGroup) throws CallStateException { 8740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.setAudioGroup(audioGroup); 8750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.ACTIVE); 8760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 8770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.continueCall(TIMEOUT_HOLD_CALL); 8780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 8790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("unhold(): " + e); 8800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void setMute(boolean muted) { 8840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if ((mSipAudioCall != null) && (muted != mSipAudioCall.isMuted())) { 885f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("setState: prev muted=" + !muted + " new muted=" + muted); 8860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.toggleMute(); 8870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean getMute() { 8910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return (mSipAudioCall == null) ? false 8920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : mSipAudioCall.isMuted(); 8930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 8960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected void setState(Call.State state) { 8970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (state == mState) return; 8980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville super.setState(state); 8990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mState = state; 9000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Call.State getState() { 9040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mState; 9050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean isIncoming() { 9090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mIncoming; 9100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public String getAddress() { 9140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Phone app uses this to query caller ID. Return the original dial 9150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // number (which may be a PSTN number) instead of the peer's SIP 9160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // URI. 9170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mOriginalNumber; 9180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public SipCall getCall() { 9220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mOwner; 9230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected Phone getPhone() { 9270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mOwner.getPhone(); 9280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void hangup() throws CallStateException { 9320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 933f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("hangup: conn=" + mPeer.getUriString() 9340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ": " + mState + ": on phone " 9350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + getPhone().getPhoneName()); 9360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!mState.isAlive()) return; 9370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 9380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipAudioCall sipAudioCall = mSipAudioCall; 9390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (sipAudioCall != null) { 9400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.setListener(null); 9410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.endCall(); 9420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 9440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("hangup(): " + e); 9450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } finally { 9460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mAdapter.onCallEnded(((mState == Call.State.INCOMING) 9470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville || (mState == Call.State.WAITING)) 9480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ? DisconnectCause.INCOMING_REJECTED 9490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : DisconnectCause.LOCAL); 9500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void separate() throws CallStateException { 9560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 9570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipCall call = (getPhone() == SipPhone.this) 95822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville ? (SipCall) getBackgroundCall() 95922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville : (SipCall) getForegroundCall(); 9600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (call.getState() != Call.State.IDLE) { 9610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException( 9620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville "cannot put conn back to a call in non-idle state: " 9630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + call.getState()); 9640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 965f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("separate: conn=" 9660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + mPeer.getUriString() + " from " + mOwner + " back to " 9670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + call); 9680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // separate the AudioGroup and connection from the original call 9700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Phone originalPhone = getPhone(); 9710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = call.getAudioGroup(); // may be null 9720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville call.add(this); 9730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.setAudioGroup(audioGroup); 9740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // put the original call to bg; and the separated call becomes 9760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // fg if it was in bg 9770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville originalPhone.switchHoldingAndActive(); 9780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // start audio and notify the phone app of the state change 98022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville call = (SipCall) getForegroundCall(); 9810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.startAudio(); 9820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville call.onConnectionStateChanged(this); 9830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 986f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void log(String s) { 987f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.d(SCN_TAG, s); 9880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private abstract class SipAudioCallAdapter extends SipAudioCall.Listener { 992f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final String SACA_TAG = "SipAudioCallAdapter"; 993f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final boolean SACA_DBG = true; 994b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen /** Call ended with cause defined in {@link DisconnectCause}. */ 995b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen protected abstract void onCallEnded(int cause); 996b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen /** Call failed with cause defined in {@link DisconnectCause}. */ 997b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen protected abstract void onError(int cause); 9980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 10000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onCallEnded(SipAudioCall call) { 1001f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SACA_DBG) log("onCallEnded: call=" + call); 10020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onCallEnded(call.isInCall() 1003b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen ? DisconnectCause.NORMAL 1004b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen : DisconnectCause.INCOMING_MISSED); 10050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 10060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 10070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 10080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onCallBusy(SipAudioCall call) { 1009f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SACA_DBG) log("onCallBusy: call=" + call); 1010b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onCallEnded(DisconnectCause.BUSY); 10110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 10120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 10130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 10140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onError(SipAudioCall call, int errorCode, 10150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String errorMessage) { 1016f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SACA_DBG) { 1017f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville log("onError: call=" + call + " code="+ SipErrorCode.toString(errorCode) 1018f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville + ": " + errorMessage); 1019f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 10200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch (errorCode) { 10210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.SERVER_UNREACHABLE: 1022b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.SERVER_UNREACHABLE); 10230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.PEER_NOT_REACHABLE: 1025b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.NUMBER_UNREACHABLE); 10260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.INVALID_REMOTE_URI: 1028b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.INVALID_NUMBER); 10290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.TIME_OUT: 10310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.TRANSACTION_TERMINTED: 1032b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.TIMED_OUT); 10330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.DATA_CONNECTION_LOST: 1035b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.LOST_SIGNAL); 10360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.INVALID_CREDENTIALS: 1038b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.INVALID_CREDENTIALS); 10390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.CROSS_DOMAIN_AUTHENTICATION: 1041b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.OUT_OF_NETWORK); 10420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.SERVER_ERROR: 1044b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.SERVER_ERROR); 10450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.SOCKET_ERROR: 10470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.CLIENT_ERROR: 10480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville default: 1049b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.ERROR_UNSPECIFIED); 10500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 10510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1052f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 1053f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void log(String s) { 1054f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.d(SACA_TAG, s); 1055f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 10560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 10570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville} 1058