SipPhone.java revision 58dd6858dc8013b680ea003d22063fd65ed5fe1c
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; 29df0280231c51a24a0b66c24034827d7f73d6e1acSantos Cordonimport android.os.Bundle; 300825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.os.Message; 31b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensenimport android.telephony.DisconnectCause; 320825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.telephony.PhoneNumberUtils; 330825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.telephony.ServiceState; 340825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.text.TextUtils; 3599c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Savilleimport android.telephony.Rlog; 360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 370825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.Call; 380825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.CallStateException; 390825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.Connection; 400825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.Phone; 410825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.PhoneConstants; 420825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.PhoneNotifier; 430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 440825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.text.ParseException; 450825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.List; 460825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.regex.Pattern; 470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/** 490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * {@hide} 500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 510825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillepublic class SipPhone extends SipPhoneBase { 520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String LOG_TAG = "SipPhone"; 53f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final boolean DBG = true; 54ff4e317d24f0d23bdc0f306d53ddc51f2f1ecf6aWink Saville private static final boolean VDBG = false; // STOPSHIP if true 550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int TIMEOUT_MAKE_CALL = 15; // in seconds 560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int TIMEOUT_ANSWER_CALL = 8; // in seconds 570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int TIMEOUT_HOLD_CALL = 15; // in seconds 580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // A call that is ringing or (call) waiting 6022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville private SipCall mRingingCall = new SipCall(); 6122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville private SipCall mForegroundCall = new SipCall(); 6222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville private SipCall mBackgroundCall = new SipCall(); 630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipManager mSipManager; 650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipProfile mProfile; 660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipPhone (Context context, PhoneNotifier notifier, SipProfile profile) { 68ff4e317d24f0d23bdc0f306d53ddc51f2f1ecf6aWink Saville super("SIP:" + profile.getUriString(), context, notifier); 690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 70f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("new SipPhone: " + profile.getUriString()); 7122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall = new SipCall(); 7222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall = new SipCall(); 7322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mBackgroundCall = new SipCall(); 740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mProfile = profile; 750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipManager = SipManager.newInstance(context); 760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean equals(Object o) { 800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (o == this) return true; 810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!(o instanceof SipPhone)) return false; 820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipPhone that = (SipPhone) o; 830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mProfile.getUriString().equals(that.mProfile.getUriString()); 840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public String getSipUri() { 870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mProfile.getUriString(); 880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean equals(SipPhone phone) { 910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return getSipUri().equals(phone.getSipUri()); 920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 94e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal public Connection takeIncomingCall(Object incomingCall) { 95f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // FIXME: Is synchronizing on the class necessary, should we use a mLockObj? 96f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // Also there are many things not synchronized, of course 9758dd6858dc8013b680ea003d22063fd65ed5fe1cAmit Mahajan // this may be true of GsmCdmaPhone too!!! 980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 99f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (!(incomingCall instanceof SipAudioCall)) { 100e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (DBG) log("takeIncomingCall: ret=null, not a SipAudioCall"); 101e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal return null; 102f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 10322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mRingingCall.getState().isAlive()) { 104e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (DBG) log("takeIncomingCall: ret=null, ringingCall not alive"); 105e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal return null; 106f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 1070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: is it true that we cannot take any incoming call if 1090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // both foreground and background are active 11022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mForegroundCall.getState().isAlive() 11122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville && mBackgroundCall.getState().isAlive()) { 112f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) { 113e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal log("takeIncomingCall: ret=null," + " foreground and background both alive"); 114f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 115e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal return null; 1160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 1190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipAudioCall sipAudioCall = (SipAudioCall) incomingCall; 120e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (DBG) log("takeIncomingCall: taking call from: " 1210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + sipAudioCall.getPeerProfile().getUriString()); 1220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String localUri = sipAudioCall.getLocalProfile().getUriString(); 1230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (localUri.equals(mProfile.getUriString())) { 12422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville boolean makeCallWait = mForegroundCall.getState().isAlive(); 125e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal SipConnection connection = mRingingCall.initIncomingCall(sipAudioCall, 126e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal makeCallWait); 127e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (sipAudioCall.getState() != SipSession.State.INCOMING_CALL) { 1280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Peer cancelled the call! 129e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (DBG) log(" takeIncomingCall: call cancelled !!"); 13022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.reset(); 131e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal connection = null; 1320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 133e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal return connection; 1340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (Exception e) { 1360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Peer may cancel the call at any time during the time we hook 1370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // up ringingCall with sipAudioCall. Clean up ringingCall when 1380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // that happens. 139e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (DBG) log(" takeIncomingCall: exception e=" + e); 14022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.reset(); 1410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 142e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (DBG) log("takeIncomingCall: NOT taking !!"); 143e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal return null; 1440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 147f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 1486d05f561549a66b597a5119665ccc3bf8a962d16Andrew Lee public void acceptCall(int videoState) throws CallStateException { 1490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 15022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if ((mRingingCall.getState() == Call.State.INCOMING) || 15122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville (mRingingCall.getState() == Call.State.WAITING)) { 152f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("acceptCall: accepting"); 1530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Always unmute when answering a new call 15422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.setMute(false); 15522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.acceptCall(); 1560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 157f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) { 158f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville log("acceptCall:" + 159f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville " throw CallStateException(\"phone not ringing\")"); 160f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 1610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("phone not ringing"); 1620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 166f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 1670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void rejectCall() throws CallStateException { 1680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 16922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mRingingCall.getState().isRinging()) { 170f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("rejectCall: rejecting"); 17122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.rejectCall(); 1720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 173f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) { 174f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville log("rejectCall:" + 175f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville " throw CallStateException(\"phone not ringing\")"); 176f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 1770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("phone not ringing"); 1780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 182f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 1836bbcbfd62c9aa5787e7c33936e2246ff05b59d58Tyler Gunn public Connection dial(String dialString, int videoState) throws CallStateException { 1840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 1856bbcbfd62c9aa5787e7c33936e2246ff05b59d58Tyler Gunn return dialInternal(dialString, videoState); 1860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1896bbcbfd62c9aa5787e7c33936e2246ff05b59d58Tyler Gunn private Connection dialInternal(String dialString, int videoState) 1900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throws CallStateException { 191f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("dialInternal: dialString=" + (VDBG ? dialString : "xxxxxx")); 1920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville clearDisconnected(); 1930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!canDial()) { 195f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville throw new CallStateException("dialInternal: cannot dial in current state"); 1960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 19722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mForegroundCall.getState() == SipCall.State.ACTIVE) { 1980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switchHoldingAndActive(); 1990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 20022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mForegroundCall.getState() != SipCall.State.IDLE) { 2010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville //we should have failed in !canDial() above before we get here 2020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("cannot dial in current state"); 2030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 20522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.setMute(false); 2060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 20722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville Connection c = mForegroundCall.dial(dialString); 2080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return c; 2090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 210f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville loge("dialInternal: ", e); 2110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("dial error: " + e); 2120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 215f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void switchHoldingAndActive() throws CallStateException { 217df0280231c51a24a0b66c24034827d7f73d6e1acSantos Cordon if (DBG) log("switchHoldingAndActive: switch fg and bg"); 2180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 21922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.switchWith(mBackgroundCall); 22022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mBackgroundCall.getState().isAlive()) mBackgroundCall.hold(); 22122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mForegroundCall.getState().isAlive()) mForegroundCall.unhold(); 2220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 225f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean canConference() { 227f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("canConference: ret=true"); 2280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return true; 2290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 231f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void conference() throws CallStateException { 2330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 23422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if ((mForegroundCall.getState() != SipCall.State.ACTIVE) 23522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville || (mForegroundCall.getState() != SipCall.State.ACTIVE)) { 2360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("wrong state to merge calls: fg=" 23722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville + mForegroundCall.getState() + ", bg=" 23822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville + mBackgroundCall.getState()); 2390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 240f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("conference: merge fg & bg"); 24122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.merge(mBackgroundCall); 2420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void conference(Call that) throws CallStateException { 2460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 2470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!(that instanceof SipCall)) { 2480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("expect " + SipCall.class 2490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ", cannot merge with " + that.getClass()); 2500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 25122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.merge((SipCall) that); 2520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 255f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean canTransfer() { 2570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return false; 2580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 260f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 261cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville public void explicitCallTransfer() { 2620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville //mCT.explicitCallTransfer(); 2630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 265f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void clearDisconnected() { 2670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 26822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.clearDisconnected(); 26922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.clearDisconnected(); 27022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mBackgroundCall.clearDisconnected(); 2710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville updatePhoneState(); 2730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville notifyPreciseCallStateChanged(); 2740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 277f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void sendDtmf(char c) { 2790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!PhoneNumberUtils.is12Key(c)) { 280f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville loge("sendDtmf called with invalid character '" + c + "'"); 28122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville } else if (mForegroundCall.getState().isAlive()) { 2820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 28322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.sendDtmf(c); 2840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 288f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void startDtmf(char c) { 2900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!PhoneNumberUtils.is12Key(c)) { 291f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville loge("startDtmf called with invalid character '" + c + "'"); 2920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 2930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sendDtmf(c); 2940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 297f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void stopDtmf() { 2990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // no op 3000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void sendBurstDtmf(String dtmfString) { 303f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville loge("sendBurstDtmf() is a CDMA method"); 3040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 306f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void getOutgoingCallerIdDisplay(Message onComplete) { 3080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: what to reply? 3090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AsyncResult.forMessage(onComplete, null, null); 3100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onComplete.sendToTarget(); 3110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 313f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, 3150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Message onComplete) { 3160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: what's this for SIP? 3170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AsyncResult.forMessage(onComplete, null, null); 3180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onComplete.sendToTarget(); 3190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 321f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void getCallWaiting(Message onComplete) { 3230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: what to reply? 3240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AsyncResult.forMessage(onComplete, null, null); 3250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onComplete.sendToTarget(); 3260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 328f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void setCallWaiting(boolean enable, Message onComplete) { 3300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: what to reply? 331f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville loge("call waiting not supported"); 3320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 335184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota public void setEchoSuppressionEnabled() { 336184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota // Echo suppression may not be available on every device. So, check 337184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota // whether it is supported 3380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 339184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 340184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota String echoSuppression = audioManager.getParameters("ec_supported"); 341184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota if (echoSuppression.contains("off")) { 342184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota mForegroundCall.setAudioGroupMode(); 343184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota } 3440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 347f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void setMute(boolean muted) { 3490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 35022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.setMute(muted); 3510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 354f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean getMute() { 35622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return (mForegroundCall.getState().isAlive() 35722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville ? mForegroundCall.getMute() 35822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville : mBackgroundCall.getMute()); 3590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 361f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Call getForegroundCall() { 36322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return mForegroundCall; 3640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 366f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Call getBackgroundCall() { 36822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return mBackgroundCall; 3690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 371f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Call getRingingCall() { 37322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return mRingingCall; 3740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 376f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public ServiceState getServiceState() { 3780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: we may need to provide this when data connectivity is lost 3790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // or when server is down 3800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return super.getServiceState(); 3810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private String getUriString(SipProfile p) { 3840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // SipProfile.getUriString() may contain "SIP:" and port 3850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return p.getUserName() + "@" + getSipDomain(p); 3860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private String getSipDomain(SipProfile p) { 3890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String domain = p.getSipDomain(); 3900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // TODO: move this to SipProfile 3910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (domain.endsWith(":5060")) { 3920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return domain.substring(0, domain.length() - 5); 3930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 3940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return domain; 3950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 398f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static Call.State getCallStateFrom(SipAudioCall sipAudioCall) { 399f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (sipAudioCall.isOnHold()) return Call.State.HOLDING; 400f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville int sessionState = sipAudioCall.getState(); 401f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville switch (sessionState) { 402f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.READY_TO_CALL: return Call.State.IDLE; 403f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.INCOMING_CALL: 404f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.INCOMING_CALL_ANSWERING: return Call.State.INCOMING; 405f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.OUTGOING_CALL: return Call.State.DIALING; 406f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.OUTGOING_CALL_RING_BACK: return Call.State.ALERTING; 407f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.OUTGOING_CALL_CANCELING: return Call.State.DISCONNECTING; 408f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.IN_CALL: return Call.State.ACTIVE; 409f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville default: 410f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville slog("illegal connection state: " + sessionState); 411f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return Call.State.DISCONNECTED; 412f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 413f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 414f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 415f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void log(String s) { 416f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.d(LOG_TAG, s); 417f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 418f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 419f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static void slog(String s) { 420f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.d(LOG_TAG, s); 421f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 422f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 423f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void loge(String s) { 424f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.e(LOG_TAG, s); 425f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 426f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 427f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void loge(String s, Exception e) { 428f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.e(LOG_TAG, s, e); 429f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 430f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 4310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private class SipCall extends SipCallBase { 432f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final String SC_TAG = "SipCall"; 433f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final boolean SC_DBG = true; 434f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final boolean SC_VDBG = false; // STOPSHIP if true 435f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 4360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void reset() { 437f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("reset"); 43822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mConnections.clear(); 4390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.IDLE); 4400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void switchWith(SipCall that) { 443f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("switchWith"); 4440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 4450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipCall tmp = new SipCall(); 4460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville tmp.takeOver(this); 4470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville this.takeOver(that); 4480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville that.takeOver(tmp); 4490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void takeOver(SipCall that) { 453f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("takeOver"); 45422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mConnections = that.mConnections; 45522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mState = that.mState; 45622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) { 4570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ((SipConnection) c).changeOwner(this); 4580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 4620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Phone getPhone() { 4630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return SipPhone.this; 4640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 4670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public List<Connection> getConnections() { 468f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_VDBG) log("getConnections"); 4690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 4700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME should return Collections.unmodifiableList(); 47122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return mConnections; 4720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Connection dial(String originalNumber) throws SipException { 476f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("dial: num=" + (SC_VDBG ? originalNumber : "xxx")); 477f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // TODO: Should this be synchronized? 4780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String calleeSipUri = originalNumber; 4790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!calleeSipUri.contains("@")) { 4800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String replaceStr = Pattern.quote(mProfile.getUserName() + "@"); 4810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville calleeSipUri = mProfile.getUriString().replaceFirst(replaceStr, 4820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville calleeSipUri + "@"); 4830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 4850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipProfile callee = 4860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville new SipProfile.Builder(calleeSipUri).build(); 4870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipConnection c = new SipConnection(this, callee, 4880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville originalNumber); 4890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville c.dial(); 49022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mConnections.add(c); 4910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.DIALING); 4920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return c; 4930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (ParseException e) { 4940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new SipException("dial", e); 4950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 4990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void hangup() throws CallStateException { 5000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 50122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mState.isAlive()) { 502f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("hangup: call " + getState() 5030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ": " + this + " on phone " + getPhone()); 5040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(State.DISCONNECTING); 5050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville CallStateException excp = null; 50622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) { 5070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 5080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville c.hangup(); 5090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (CallStateException e) { 5100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville excp = e; 5110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (excp != null) throw excp; 5140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 515f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("hangup: dead call " + getState() 5160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ": " + this + " on phone " + getPhone()); 5170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 521e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal SipConnection initIncomingCall(SipAudioCall sipAudioCall, boolean makeCallWait) { 5220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipProfile callee = sipAudioCall.getPeerProfile(); 5230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipConnection c = new SipConnection(this, callee); 52422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mConnections.add(c); 5250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Call.State newState = makeCallWait ? State.WAITING : State.INCOMING; 5270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville c.initIncomingCall(sipAudioCall, newState); 5280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(newState); 5300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville notifyNewRingingConnectionP(c); 531e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal return c; 5320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void rejectCall() throws CallStateException { 535f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("rejectCall:"); 5360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville hangup(); 5370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void acceptCall() throws CallStateException { 540f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("acceptCall: accepting"); 54122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (this != mRingingCall) { 5420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("acceptCall() in a non-ringing call"); 5430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 54422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mConnections.size() != 1) { 5450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("acceptCall() in a conf call"); 5460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 54722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville ((SipConnection) mConnections.get(0)).acceptCall(); 5480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private boolean isSpeakerOn() { 551f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Boolean ret = ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)) 5520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville .isSpeakerphoneOn(); 553f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_VDBG) log("isSpeakerOn: ret=" + ret); 554f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return ret; 5550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void setAudioGroupMode() { 5580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = getAudioGroup(); 559f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (audioGroup == null) { 560f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("setAudioGroupMode: audioGroup == null ignore"); 561f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return; 562f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 5630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int mode = audioGroup.getMode(); 56422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mState == State.HOLDING) { 5650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.setMode(AudioGroup.MODE_ON_HOLD); 5660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else if (getMute()) { 5670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.setMode(AudioGroup.MODE_MUTED); 5680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else if (isSpeakerOn()) { 5690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.setMode(AudioGroup.MODE_ECHO_SUPPRESSION); 5700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 5710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.setMode(AudioGroup.MODE_NORMAL); 5720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 573f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log(String.format( 574f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville "setAudioGroupMode change: %d --> %d", mode, 5750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.getMode())); 5760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void hold() throws CallStateException { 579f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("hold:"); 5800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(State.HOLDING); 58122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) ((SipConnection) c).hold(); 5820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setAudioGroupMode(); 5830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void unhold() throws CallStateException { 586f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("unhold:"); 5870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(State.ACTIVE); 5880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = new AudioGroup(); 58922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) { 5900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ((SipConnection) c).unhold(audioGroup); 5910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setAudioGroupMode(); 5930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void setMute(boolean muted) { 596f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("setMute: muted=" + muted); 59722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) { 5980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ((SipConnection) c).setMute(muted); 5990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean getMute() { 60322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville boolean ret = mConnections.isEmpty() 6040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ? false 60522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville : ((SipConnection) mConnections.get(0)).getMute(); 606f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("getMute: ret=" + ret); 607f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return ret; 6080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void merge(SipCall that) throws CallStateException { 611f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("merge:"); 6120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = getAudioGroup(); 6130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // copy to an array to avoid concurrent modification as connections 6150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // in that.connections will be removed in add(SipConnection). 61622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville Connection[] cc = that.mConnections.toArray( 61722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville new Connection[that.mConnections.size()]); 6180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (Connection c : cc) { 6190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipConnection conn = (SipConnection) c; 6200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville add(conn); 6210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (conn.getState() == Call.State.HOLDING) { 6220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville conn.unhold(audioGroup); 6230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville that.setState(Call.State.IDLE); 6260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void add(SipConnection conn) { 629f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("add:"); 6300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipCall call = conn.getCall(); 6310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (call == this) return; 63222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (call != null) call.mConnections.remove(conn); 6330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 63422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mConnections.add(conn); 6350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville conn.changeOwner(this); 6360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void sendDtmf(char c) { 639f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("sendDtmf: c=" + c); 6400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = getAudioGroup(); 641f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (audioGroup == null) { 642f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("sendDtmf: audioGroup == null, ignore c=" + c); 643f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return; 644f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 6450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.sendDtmf(convertDtmf(c)); 6460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private int convertDtmf(char c) { 6490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int code = c - '0'; 6500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if ((code < 0) || (code > 9)) { 6510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch (c) { 6520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case '*': return 10; 6530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case '#': return 11; 6540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 'A': return 12; 6550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 'B': return 13; 6560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 'C': return 14; 6570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 'D': return 15; 6580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville default: 6590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new IllegalArgumentException( 6600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville "invalid DTMF char: " + (int) c); 6610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return code; 6640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 6670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected void setState(State newState) { 66822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mState != newState) { 66922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (SC_DBG) log("setState: cur state" + mState 6700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + " --> " + newState + ": " + this + ": on phone " 67122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville + getPhone() + " " + mConnections.size()); 6720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (newState == Call.State.ALERTING) { 67422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mState = newState; // need in ALERTING to enable ringback 67522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville startRingbackTone(); 67622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville } else if (mState == Call.State.ALERTING) { 67722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville stopRingbackTone(); 6780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 67922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mState = newState; 6800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville updatePhoneState(); 6810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville notifyPreciseCallStateChanged(); 6820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void onConnectionStateChanged(SipConnection conn) { 6860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // this can be called back when a conf call is formed 687f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("onConnectionStateChanged: conn=" + conn); 68822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mState != State.ACTIVE) { 6890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(conn.getState()); 6900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void onConnectionEnded(SipConnection conn) { 6940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // set state to DISCONNECTED only when all conns are disconnected 695f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("onConnectionEnded: conn=" + conn); 69622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mState != State.DISCONNECTED) { 6970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean allConnectionsDisconnected = true; 698f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("---check connections: " 69922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville + mConnections.size()); 70022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) { 701f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log(" state=" + c.getState() + ": " 7020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + c); 7030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (c.getState() != State.DISCONNECTED) { 7040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville allConnectionsDisconnected = false; 7050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 7060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (allConnectionsDisconnected) setState(State.DISCONNECTED); 7090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville notifyDisconnectP(conn); 7110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private AudioGroup getAudioGroup() { 71422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mConnections.isEmpty()) return null; 71522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return ((SipConnection) mConnections.get(0)).getAudioGroup(); 7160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 717f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 718f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void log(String s) { 719f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.d(SC_TAG, s); 720f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 7210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private class SipConnection extends SipConnectionBase { 724f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final String SCN_TAG = "SipConnection"; 725f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final boolean SCN_DBG = true; 726f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 7270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipCall mOwner; 7280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipAudioCall mSipAudioCall; 7290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private Call.State mState = Call.State.IDLE; 7300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipProfile mPeer; 7310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private boolean mIncoming = false; 732f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private String mOriginalNumber; // may be a PSTN number 7330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipAudioCallAdapter mAdapter = new SipAudioCallAdapter() { 7350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 736b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen protected void onCallEnded(int cause) { 7370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (getDisconnectCause() != DisconnectCause.LOCAL) { 7380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setDisconnectCause(cause); 7390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 7410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.DISCONNECTED); 7420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipAudioCall sipAudioCall = mSipAudioCall; 743f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // FIXME: This goes null and is synchronized, but many uses aren't sync'd 7440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall = null; 7450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String sessionState = (sipAudioCall == null) 7460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ? "" 7470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : (sipAudioCall.getState() + ", "); 748f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("[SipAudioCallAdapter] onCallEnded: " 7490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + mPeer.getUriString() + ": " + sessionState 7500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + "cause: " + getDisconnectCause() + ", on phone " 7510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + getPhone()); 7520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (sipAudioCall != null) { 7530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.setListener(null); 7540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.close(); 7550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOwner.onConnectionEnded(SipConnection.this); 7570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 7610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onCallEstablished(SipAudioCall call) { 7620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onChanged(call); 763f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // Race onChanged synchronized this isn't 7640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mState == Call.State.ACTIVE) call.startAudio(); 7650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 7680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onCallHeld(SipAudioCall call) { 7690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onChanged(call); 770f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // Race onChanged synchronized this isn't 7710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mState == Call.State.HOLDING) call.startAudio(); 7720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 7750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onChanged(SipAudioCall call) { 7760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 7770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Call.State newState = getCallStateFrom(call); 7780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mState == newState) return; 7790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (newState == Call.State.INCOMING) { 7800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(mOwner.getState()); // INCOMING or WAITING 7810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 78222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mOwner == mRingingCall) { 78322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mRingingCall.getState() == Call.State.WAITING) { 7840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 7850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switchHoldingAndActive(); 7860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (CallStateException e) { 7870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // disconnect the call. 7880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onCallEnded(DisconnectCause.LOCAL); 7890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return; 7900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 79222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.switchWith(mRingingCall); 7930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(newState); 7950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOwner.onConnectionStateChanged(SipConnection.this); 797f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("onChanged: " 7980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + mPeer.getUriString() + ": " + mState 7990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + " on phone " + getPhone()); 8000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 804b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen protected void onError(int cause) { 805f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("onError: " + cause); 8060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onCallEnded(cause); 8070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville }; 8090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public SipConnection(SipCall owner, SipProfile callee, 8110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String originalNumber) { 8120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville super(originalNumber); 8130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOwner = owner; 8140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mPeer = callee; 8150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOriginalNumber = originalNumber; 8160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public SipConnection(SipCall owner, SipProfile callee) { 8190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville this(owner, callee, getUriString(callee)); 8200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 8230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public String getCnapName() { 8240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String displayName = mPeer.getDisplayName(); 8250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return TextUtils.isEmpty(displayName) ? null 8260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : displayName; 8270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 8300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public int getNumberPresentation() { 8310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return PhoneConstants.PRESENTATION_ALLOWED; 8320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void initIncomingCall(SipAudioCall sipAudioCall, Call.State newState) { 8350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(newState); 8360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall = sipAudioCall; 8370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.setListener(mAdapter); // call back to set state 8380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mIncoming = true; 8390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void acceptCall() throws CallStateException { 8420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 8430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.answerCall(TIMEOUT_ANSWER_CALL); 8440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 8450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("acceptCall(): " + e); 8460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void changeOwner(SipCall owner) { 8500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOwner = owner; 8510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup getAudioGroup() { 8540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mSipAudioCall == null) return null; 8550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mSipAudioCall.getAudioGroup(); 8560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void dial() throws SipException { 8590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.DIALING); 8600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall = mSipManager.makeAudioCall(mProfile, mPeer, null, 8610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville TIMEOUT_MAKE_CALL); 8620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.setListener(mAdapter); 8630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void hold() throws CallStateException { 8660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.HOLDING); 8670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 8680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.holdCall(TIMEOUT_HOLD_CALL); 8690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 8700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("hold(): " + e); 8710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void unhold(AudioGroup audioGroup) throws CallStateException { 8750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.setAudioGroup(audioGroup); 8760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.ACTIVE); 8770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 8780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.continueCall(TIMEOUT_HOLD_CALL); 8790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 8800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("unhold(): " + e); 8810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void setMute(boolean muted) { 8850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if ((mSipAudioCall != null) && (muted != mSipAudioCall.isMuted())) { 886f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("setState: prev muted=" + !muted + " new muted=" + muted); 8870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.toggleMute(); 8880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean getMute() { 8920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return (mSipAudioCall == null) ? false 8930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : mSipAudioCall.isMuted(); 8940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 8970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected void setState(Call.State state) { 8980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (state == mState) return; 8990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville super.setState(state); 9000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mState = state; 9010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Call.State getState() { 9050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mState; 9060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean isIncoming() { 9100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mIncoming; 9110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public String getAddress() { 9150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Phone app uses this to query caller ID. Return the original dial 9160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // number (which may be a PSTN number) instead of the peer's SIP 9170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // URI. 9180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mOriginalNumber; 9190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public SipCall getCall() { 9230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mOwner; 9240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected Phone getPhone() { 9280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mOwner.getPhone(); 9290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void hangup() throws CallStateException { 9330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 934f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("hangup: conn=" + mPeer.getUriString() 9350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ": " + mState + ": on phone " 9360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + getPhone().getPhoneName()); 9370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!mState.isAlive()) return; 9380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 9390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipAudioCall sipAudioCall = mSipAudioCall; 9400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (sipAudioCall != null) { 9410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.setListener(null); 9420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.endCall(); 9430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 9450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("hangup(): " + e); 9460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } finally { 9470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mAdapter.onCallEnded(((mState == Call.State.INCOMING) 9480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville || (mState == Call.State.WAITING)) 9490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ? DisconnectCause.INCOMING_REJECTED 9500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : DisconnectCause.LOCAL); 9510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void separate() throws CallStateException { 9570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 9580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipCall call = (getPhone() == SipPhone.this) 95922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville ? (SipCall) getBackgroundCall() 96022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville : (SipCall) getForegroundCall(); 9610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (call.getState() != Call.State.IDLE) { 9620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException( 9630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville "cannot put conn back to a call in non-idle state: " 9640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + call.getState()); 9650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 966f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("separate: conn=" 9670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + mPeer.getUriString() + " from " + mOwner + " back to " 9680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + call); 9690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // separate the AudioGroup and connection from the original call 9710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Phone originalPhone = getPhone(); 9720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = call.getAudioGroup(); // may be null 9730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville call.add(this); 9740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.setAudioGroup(audioGroup); 9750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // put the original call to bg; and the separated call becomes 9770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // fg if it was in bg 9780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville originalPhone.switchHoldingAndActive(); 9790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // start audio and notify the phone app of the state change 98122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville call = (SipCall) getForegroundCall(); 9820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.startAudio(); 9830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville call.onConnectionStateChanged(this); 9840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 987f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void log(String s) { 988f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.d(SCN_TAG, s); 9890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private abstract class SipAudioCallAdapter extends SipAudioCall.Listener { 993f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final String SACA_TAG = "SipAudioCallAdapter"; 994f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final boolean SACA_DBG = true; 995b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen /** Call ended with cause defined in {@link DisconnectCause}. */ 996b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen protected abstract void onCallEnded(int cause); 997b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen /** Call failed with cause defined in {@link DisconnectCause}. */ 998b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen protected abstract void onError(int cause); 9990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 10000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 10010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onCallEnded(SipAudioCall call) { 1002f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SACA_DBG) log("onCallEnded: call=" + call); 10030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onCallEnded(call.isInCall() 1004b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen ? DisconnectCause.NORMAL 1005b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen : DisconnectCause.INCOMING_MISSED); 10060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 10070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 10080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 10090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onCallBusy(SipAudioCall call) { 1010f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SACA_DBG) log("onCallBusy: call=" + call); 1011b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onCallEnded(DisconnectCause.BUSY); 10120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 10130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 10140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 10150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onError(SipAudioCall call, int errorCode, 10160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String errorMessage) { 1017f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SACA_DBG) { 1018f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville log("onError: call=" + call + " code="+ SipErrorCode.toString(errorCode) 1019f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville + ": " + errorMessage); 1020f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 10210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch (errorCode) { 10220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.SERVER_UNREACHABLE: 1023b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.SERVER_UNREACHABLE); 10240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.PEER_NOT_REACHABLE: 1026b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.NUMBER_UNREACHABLE); 10270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.INVALID_REMOTE_URI: 1029b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.INVALID_NUMBER); 10300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.TIME_OUT: 10320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.TRANSACTION_TERMINTED: 1033b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.TIMED_OUT); 10340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.DATA_CONNECTION_LOST: 1036b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.LOST_SIGNAL); 10370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.INVALID_CREDENTIALS: 1039b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.INVALID_CREDENTIALS); 10400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.CROSS_DOMAIN_AUTHENTICATION: 1042b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.OUT_OF_NETWORK); 10430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.SERVER_ERROR: 1045b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.SERVER_ERROR); 10460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.SOCKET_ERROR: 10480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.CLIENT_ERROR: 10490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville default: 1050b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.ERROR_UNSPECIFIED); 10510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 10520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1053f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 1054f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void log(String s) { 1055f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.d(SACA_TAG, s); 1056f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 10570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 10580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville} 1059