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 5799a3e79b952978f781c1178010129231701e231bBrad Ebinger // Minimum time needed between hold/unhold requests. 5899a3e79b952978f781c1178010129231701e231bBrad Ebinger private static final long TIMEOUT_HOLD_PROCESSING = 1000; // ms 590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // A call that is ringing or (call) waiting 6122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville private SipCall mRingingCall = new SipCall(); 6222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville private SipCall mForegroundCall = new SipCall(); 6322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville private SipCall mBackgroundCall = new SipCall(); 640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipManager mSipManager; 660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipProfile mProfile; 670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6899a3e79b952978f781c1178010129231701e231bBrad Ebinger private long mTimeOfLastValidHoldRequest = System.currentTimeMillis(); 6999a3e79b952978f781c1178010129231701e231bBrad Ebinger 700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipPhone (Context context, PhoneNotifier notifier, SipProfile profile) { 71ff4e317d24f0d23bdc0f306d53ddc51f2f1ecf6aWink Saville super("SIP:" + profile.getUriString(), context, notifier); 720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 73b4350195add6b77d9352a41e1a8e5580a6a4816cSantos Cordon if (DBG) log("new SipPhone: " + hidePii(profile.getUriString())); 7422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall = new SipCall(); 7522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall = new SipCall(); 7622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mBackgroundCall = new SipCall(); 770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mProfile = profile; 780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipManager = SipManager.newInstance(context); 790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean equals(Object o) { 830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (o == this) return true; 840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!(o instanceof SipPhone)) return false; 850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipPhone that = (SipPhone) o; 860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mProfile.getUriString().equals(that.mProfile.getUriString()); 870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public String getSipUri() { 900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mProfile.getUriString(); 910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean equals(SipPhone phone) { 940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return getSipUri().equals(phone.getSipUri()); 950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 97e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal public Connection takeIncomingCall(Object incomingCall) { 98f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // FIXME: Is synchronizing on the class necessary, should we use a mLockObj? 99f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // Also there are many things not synchronized, of course 10058dd6858dc8013b680ea003d22063fd65ed5fe1cAmit Mahajan // this may be true of GsmCdmaPhone too!!! 1010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 102f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (!(incomingCall instanceof SipAudioCall)) { 103e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (DBG) log("takeIncomingCall: ret=null, not a SipAudioCall"); 104e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal return null; 105f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 10622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mRingingCall.getState().isAlive()) { 107e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (DBG) log("takeIncomingCall: ret=null, ringingCall not alive"); 108e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal return null; 109f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 1100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: is it true that we cannot take any incoming call if 1120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // both foreground and background are active 11322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mForegroundCall.getState().isAlive() 11422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville && mBackgroundCall.getState().isAlive()) { 115f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) { 116e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal log("takeIncomingCall: ret=null," + " foreground and background both alive"); 117f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 118e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal return null; 1190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 1220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipAudioCall sipAudioCall = (SipAudioCall) incomingCall; 123e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (DBG) log("takeIncomingCall: taking call from: " 124f0e497e8cf789cf47bd60a68ac12e5e22dbbe855Shunta Sakai + hidePii(sipAudioCall.getPeerProfile().getUriString())); 1250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String localUri = sipAudioCall.getLocalProfile().getUriString(); 1260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (localUri.equals(mProfile.getUriString())) { 12722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville boolean makeCallWait = mForegroundCall.getState().isAlive(); 128e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal SipConnection connection = mRingingCall.initIncomingCall(sipAudioCall, 129e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal makeCallWait); 130e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (sipAudioCall.getState() != SipSession.State.INCOMING_CALL) { 1310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Peer cancelled the call! 132e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (DBG) log(" takeIncomingCall: call cancelled !!"); 13322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.reset(); 134e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal connection = null; 1350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 136e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal return connection; 1370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (Exception e) { 1390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Peer may cancel the call at any time during the time we hook 1400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // up ringingCall with sipAudioCall. Clean up ringingCall when 1410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // that happens. 142e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (DBG) log(" takeIncomingCall: exception e=" + e); 14322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.reset(); 1440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 145e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal if (DBG) log("takeIncomingCall: NOT taking !!"); 146e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal return null; 1470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 150f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 1516d05f561549a66b597a5119665ccc3bf8a962d16Andrew Lee public void acceptCall(int videoState) throws CallStateException { 1520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 15322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if ((mRingingCall.getState() == Call.State.INCOMING) || 15422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville (mRingingCall.getState() == Call.State.WAITING)) { 155f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("acceptCall: accepting"); 1560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Always unmute when answering a new call 15722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.setMute(false); 15822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.acceptCall(); 1590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 160f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) { 161f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville log("acceptCall:" + 162f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville " throw CallStateException(\"phone not ringing\")"); 163f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 1640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("phone not ringing"); 1650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 169f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 1700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void rejectCall() throws CallStateException { 1710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 17222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mRingingCall.getState().isRinging()) { 173f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("rejectCall: rejecting"); 17422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.rejectCall(); 1750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 176f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) { 177f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville log("rejectCall:" + 178f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville " throw CallStateException(\"phone not ringing\")"); 179f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 1800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("phone not ringing"); 1810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 185f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 1866bbcbfd62c9aa5787e7c33936e2246ff05b59d58Tyler Gunn public Connection dial(String dialString, int videoState) throws CallStateException { 1870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 1886bbcbfd62c9aa5787e7c33936e2246ff05b59d58Tyler Gunn return dialInternal(dialString, videoState); 1890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1926bbcbfd62c9aa5787e7c33936e2246ff05b59d58Tyler Gunn private Connection dialInternal(String dialString, int videoState) 1930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throws CallStateException { 194b4350195add6b77d9352a41e1a8e5580a6a4816cSantos Cordon if (DBG) log("dialInternal: dialString=" + hidePii(dialString)); 1950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville clearDisconnected(); 1960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!canDial()) { 198f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville throw new CallStateException("dialInternal: cannot dial in current state"); 1990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 20022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mForegroundCall.getState() == SipCall.State.ACTIVE) { 2010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switchHoldingAndActive(); 2020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 20322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mForegroundCall.getState() != SipCall.State.IDLE) { 2040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville //we should have failed in !canDial() above before we get here 2050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("cannot dial in current state"); 2060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 20822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.setMute(false); 2090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 21022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville Connection c = mForegroundCall.dial(dialString); 2110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return c; 2120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 213f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville loge("dialInternal: ", e); 2140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("dial error: " + e); 2150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 218f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void switchHoldingAndActive() throws CallStateException { 22099a3e79b952978f781c1178010129231701e231bBrad Ebinger // Wait for at least TIMEOUT_HOLD_PROCESSING ms to occur before sending hold/unhold requests 22199a3e79b952978f781c1178010129231701e231bBrad Ebinger // to prevent spamming the SipAudioCall state machine and putting it into an invalid state. 22299a3e79b952978f781c1178010129231701e231bBrad Ebinger if (!isHoldTimeoutExpired()) { 22399a3e79b952978f781c1178010129231701e231bBrad Ebinger if (DBG) log("switchHoldingAndActive: Disregarded! Under " + TIMEOUT_HOLD_PROCESSING + 22499a3e79b952978f781c1178010129231701e231bBrad Ebinger " ms..."); 22599a3e79b952978f781c1178010129231701e231bBrad Ebinger return; 22699a3e79b952978f781c1178010129231701e231bBrad Ebinger } 227df0280231c51a24a0b66c24034827d7f73d6e1acSantos Cordon if (DBG) log("switchHoldingAndActive: switch fg and bg"); 2280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 22922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.switchWith(mBackgroundCall); 23022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mBackgroundCall.getState().isAlive()) mBackgroundCall.hold(); 23122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mForegroundCall.getState().isAlive()) mForegroundCall.unhold(); 2320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 235f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean canConference() { 237f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("canConference: ret=true"); 2380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return true; 2390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 241f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void conference() throws CallStateException { 2430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 24422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if ((mForegroundCall.getState() != SipCall.State.ACTIVE) 24522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville || (mForegroundCall.getState() != SipCall.State.ACTIVE)) { 2460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("wrong state to merge calls: fg=" 24722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville + mForegroundCall.getState() + ", bg=" 24822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville + mBackgroundCall.getState()); 2490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 250f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("conference: merge fg & bg"); 25122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.merge(mBackgroundCall); 2520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void conference(Call that) throws CallStateException { 2560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 2570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!(that instanceof SipCall)) { 2580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("expect " + SipCall.class 2590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ", cannot merge with " + that.getClass()); 2600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 26122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.merge((SipCall) that); 2620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 265f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean canTransfer() { 2670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return false; 2680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 270f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 271cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville public void explicitCallTransfer() { 2720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville //mCT.explicitCallTransfer(); 2730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 275f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void clearDisconnected() { 2770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 27822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.clearDisconnected(); 27922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.clearDisconnected(); 28022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mBackgroundCall.clearDisconnected(); 2810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville updatePhoneState(); 2830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville notifyPreciseCallStateChanged(); 2840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 287f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void sendDtmf(char c) { 2890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!PhoneNumberUtils.is12Key(c)) { 290f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville loge("sendDtmf called with invalid character '" + c + "'"); 29122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville } else if (mForegroundCall.getState().isAlive()) { 2920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 29322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.sendDtmf(c); 2940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 298f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void startDtmf(char c) { 3000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!PhoneNumberUtils.is12Key(c)) { 301f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville loge("startDtmf called with invalid character '" + c + "'"); 3020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 3030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sendDtmf(c); 3040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 307f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void stopDtmf() { 3090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // no op 3100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void sendBurstDtmf(String dtmfString) { 313f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville loge("sendBurstDtmf() is a CDMA method"); 3140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 316f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void getOutgoingCallerIdDisplay(Message onComplete) { 3180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: what to reply? 3190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AsyncResult.forMessage(onComplete, null, null); 3200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onComplete.sendToTarget(); 3210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 323f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, 3250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Message onComplete) { 3260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: what's this for SIP? 3270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AsyncResult.forMessage(onComplete, null, null); 3280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onComplete.sendToTarget(); 3290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 331f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void getCallWaiting(Message onComplete) { 3330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: what to reply? 3340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AsyncResult.forMessage(onComplete, null, null); 3350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onComplete.sendToTarget(); 3360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 338f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void setCallWaiting(boolean enable, Message onComplete) { 3400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: what to reply? 341f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville loge("call waiting not supported"); 3420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 345184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota public void setEchoSuppressionEnabled() { 346184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota // Echo suppression may not be available on every device. So, check 347184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota // whether it is supported 3480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 349184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 350184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota String echoSuppression = audioManager.getParameters("ec_supported"); 351184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota if (echoSuppression.contains("off")) { 352184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota mForegroundCall.setAudioGroupMode(); 353184ffb58812108624ea0be2732cdae511b99d09dVidyakumar Athota } 3540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 357f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void setMute(boolean muted) { 3590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 36022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.setMute(muted); 3610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 364f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean getMute() { 36622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return (mForegroundCall.getState().isAlive() 36722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville ? mForegroundCall.getMute() 36822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville : mBackgroundCall.getMute()); 3690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 371f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Call getForegroundCall() { 37322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return mForegroundCall; 3740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 376f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Call getBackgroundCall() { 37822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return mBackgroundCall; 3790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 381f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Call getRingingCall() { 38322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return mRingingCall; 3840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 386f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public ServiceState getServiceState() { 3880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: we may need to provide this when data connectivity is lost 3890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // or when server is down 3900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return super.getServiceState(); 3910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private String getUriString(SipProfile p) { 3940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // SipProfile.getUriString() may contain "SIP:" and port 3950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return p.getUserName() + "@" + getSipDomain(p); 3960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private String getSipDomain(SipProfile p) { 3990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String domain = p.getSipDomain(); 4000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // TODO: move this to SipProfile 4010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (domain.endsWith(":5060")) { 4020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return domain.substring(0, domain.length() - 5); 4030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 4040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return domain; 4050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 408f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static Call.State getCallStateFrom(SipAudioCall sipAudioCall) { 409f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (sipAudioCall.isOnHold()) return Call.State.HOLDING; 410f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville int sessionState = sipAudioCall.getState(); 411f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville switch (sessionState) { 412f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.READY_TO_CALL: return Call.State.IDLE; 413f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.INCOMING_CALL: 414f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.INCOMING_CALL_ANSWERING: return Call.State.INCOMING; 415f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.OUTGOING_CALL: return Call.State.DIALING; 416f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.OUTGOING_CALL_RING_BACK: return Call.State.ALERTING; 417f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.OUTGOING_CALL_CANCELING: return Call.State.DISCONNECTING; 418f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.IN_CALL: return Call.State.ACTIVE; 419f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville default: 420f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville slog("illegal connection state: " + sessionState); 421f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return Call.State.DISCONNECTED; 422f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 423f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 424f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 42599a3e79b952978f781c1178010129231701e231bBrad Ebinger private synchronized boolean isHoldTimeoutExpired() { 42699a3e79b952978f781c1178010129231701e231bBrad Ebinger long currTime = System.currentTimeMillis(); 42799a3e79b952978f781c1178010129231701e231bBrad Ebinger if ((currTime - mTimeOfLastValidHoldRequest) > TIMEOUT_HOLD_PROCESSING) { 42899a3e79b952978f781c1178010129231701e231bBrad Ebinger mTimeOfLastValidHoldRequest = currTime; 42999a3e79b952978f781c1178010129231701e231bBrad Ebinger return true; 43099a3e79b952978f781c1178010129231701e231bBrad Ebinger } 43199a3e79b952978f781c1178010129231701e231bBrad Ebinger return false; 43299a3e79b952978f781c1178010129231701e231bBrad Ebinger } 43399a3e79b952978f781c1178010129231701e231bBrad Ebinger 434f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void log(String s) { 435f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.d(LOG_TAG, s); 436f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 437f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 438f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static void slog(String s) { 439f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.d(LOG_TAG, s); 440f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 441f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 442f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void loge(String s) { 443f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.e(LOG_TAG, s); 444f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 445f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 446f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void loge(String s, Exception e) { 447f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.e(LOG_TAG, s, e); 448f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 449f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 4500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private class SipCall extends SipCallBase { 451f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final String SC_TAG = "SipCall"; 452f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final boolean SC_DBG = true; 453f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final boolean SC_VDBG = false; // STOPSHIP if true 454f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 4550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void reset() { 456f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("reset"); 45722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mConnections.clear(); 4580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.IDLE); 4590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void switchWith(SipCall that) { 462f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("switchWith"); 4630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 4640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipCall tmp = new SipCall(); 4650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville tmp.takeOver(this); 4660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville this.takeOver(that); 4670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville that.takeOver(tmp); 4680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void takeOver(SipCall that) { 472f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("takeOver"); 47322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mConnections = that.mConnections; 47422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mState = that.mState; 47522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) { 4760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ((SipConnection) c).changeOwner(this); 4770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 4810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Phone getPhone() { 4820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return SipPhone.this; 4830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 4860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public List<Connection> getConnections() { 487f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_VDBG) log("getConnections"); 4880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 4890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME should return Collections.unmodifiableList(); 49022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return mConnections; 4910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Connection dial(String originalNumber) throws SipException { 495f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("dial: num=" + (SC_VDBG ? originalNumber : "xxx")); 496f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // TODO: Should this be synchronized? 4970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String calleeSipUri = originalNumber; 4980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!calleeSipUri.contains("@")) { 4990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String replaceStr = Pattern.quote(mProfile.getUserName() + "@"); 5000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville calleeSipUri = mProfile.getUriString().replaceFirst(replaceStr, 5010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville calleeSipUri + "@"); 5020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 5040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipProfile callee = 5050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville new SipProfile.Builder(calleeSipUri).build(); 5060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipConnection c = new SipConnection(this, callee, 5070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville originalNumber); 5080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville c.dial(); 50922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mConnections.add(c); 5100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.DIALING); 5110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return c; 5120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (ParseException e) { 5130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new SipException("dial", e); 5140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 5180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void hangup() throws CallStateException { 5190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 52022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mState.isAlive()) { 521f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("hangup: call " + getState() 5220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ": " + this + " on phone " + getPhone()); 5230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(State.DISCONNECTING); 5240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville CallStateException excp = null; 52522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) { 5260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 5270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville c.hangup(); 5280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (CallStateException e) { 5290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville excp = e; 5300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (excp != null) throw excp; 5330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 534f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("hangup: dead call " + getState() 5350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ": " + this + " on phone " + getPhone()); 5360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 540e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal SipConnection initIncomingCall(SipAudioCall sipAudioCall, boolean makeCallWait) { 5410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipProfile callee = sipAudioCall.getPeerProfile(); 5420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipConnection c = new SipConnection(this, callee); 54322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mConnections.add(c); 5440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Call.State newState = makeCallWait ? State.WAITING : State.INCOMING; 5460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville c.initIncomingCall(sipAudioCall, newState); 5470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(newState); 5490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville notifyNewRingingConnectionP(c); 550e86a8d443e921caec2c398ef74f0b6d573a15bb1Sailesh Nepal return c; 5510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void rejectCall() throws CallStateException { 554f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("rejectCall:"); 5550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville hangup(); 5560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void acceptCall() throws CallStateException { 559f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("acceptCall: accepting"); 56022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (this != mRingingCall) { 5610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("acceptCall() in a non-ringing call"); 5620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 56322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mConnections.size() != 1) { 5640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("acceptCall() in a conf call"); 5650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 56622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville ((SipConnection) mConnections.get(0)).acceptCall(); 5670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private boolean isSpeakerOn() { 570f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Boolean ret = ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)) 5710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville .isSpeakerphoneOn(); 572f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_VDBG) log("isSpeakerOn: ret=" + ret); 573f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return ret; 5740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void setAudioGroupMode() { 5770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = getAudioGroup(); 578f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (audioGroup == null) { 579f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("setAudioGroupMode: audioGroup == null ignore"); 580f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return; 581f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 5820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int mode = audioGroup.getMode(); 58322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mState == State.HOLDING) { 5840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.setMode(AudioGroup.MODE_ON_HOLD); 5850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else if (getMute()) { 5860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.setMode(AudioGroup.MODE_MUTED); 5870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else if (isSpeakerOn()) { 5880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.setMode(AudioGroup.MODE_ECHO_SUPPRESSION); 5890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 5900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.setMode(AudioGroup.MODE_NORMAL); 5910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 592f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log(String.format( 593f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville "setAudioGroupMode change: %d --> %d", mode, 5940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.getMode())); 5950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void hold() throws CallStateException { 598f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("hold:"); 5990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(State.HOLDING); 60022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) ((SipConnection) c).hold(); 6010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setAudioGroupMode(); 6020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void unhold() throws CallStateException { 605f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("unhold:"); 6060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(State.ACTIVE); 6070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = new AudioGroup(); 60822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) { 6090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ((SipConnection) c).unhold(audioGroup); 6100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setAudioGroupMode(); 6120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void setMute(boolean muted) { 615f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("setMute: muted=" + muted); 61622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) { 6170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ((SipConnection) c).setMute(muted); 6180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean getMute() { 62222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville boolean ret = mConnections.isEmpty() 6230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ? false 62422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville : ((SipConnection) mConnections.get(0)).getMute(); 625f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("getMute: ret=" + ret); 626f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return ret; 6270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void merge(SipCall that) throws CallStateException { 630f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("merge:"); 6310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = getAudioGroup(); 6320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // copy to an array to avoid concurrent modification as connections 6340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // in that.connections will be removed in add(SipConnection). 63522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville Connection[] cc = that.mConnections.toArray( 63622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville new Connection[that.mConnections.size()]); 6370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (Connection c : cc) { 6380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipConnection conn = (SipConnection) c; 6390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville add(conn); 6400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (conn.getState() == Call.State.HOLDING) { 6410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville conn.unhold(audioGroup); 6420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville that.setState(Call.State.IDLE); 6450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void add(SipConnection conn) { 648f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("add:"); 6490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipCall call = conn.getCall(); 6500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (call == this) return; 65122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (call != null) call.mConnections.remove(conn); 6520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 65322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mConnections.add(conn); 6540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville conn.changeOwner(this); 6550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void sendDtmf(char c) { 658f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("sendDtmf: c=" + c); 6590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = getAudioGroup(); 660f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (audioGroup == null) { 661f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("sendDtmf: audioGroup == null, ignore c=" + c); 662f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return; 663f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 6640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.sendDtmf(convertDtmf(c)); 6650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private int convertDtmf(char c) { 6680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int code = c - '0'; 6690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if ((code < 0) || (code > 9)) { 6700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch (c) { 6710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case '*': return 10; 6720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case '#': return 11; 6730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 'A': return 12; 6740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 'B': return 13; 6750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 'C': return 14; 6760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 'D': return 15; 6770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville default: 6780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new IllegalArgumentException( 6790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville "invalid DTMF char: " + (int) c); 6800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return code; 6830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 6860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected void setState(State newState) { 68722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mState != newState) { 68822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (SC_DBG) log("setState: cur state" + mState 6890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + " --> " + newState + ": " + this + ": on phone " 69022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville + getPhone() + " " + mConnections.size()); 6910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (newState == Call.State.ALERTING) { 69322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mState = newState; // need in ALERTING to enable ringback 69422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville startRingbackTone(); 69522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville } else if (mState == Call.State.ALERTING) { 69622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville stopRingbackTone(); 6970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 69822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mState = newState; 6990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville updatePhoneState(); 7000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville notifyPreciseCallStateChanged(); 7010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void onConnectionStateChanged(SipConnection conn) { 7050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // this can be called back when a conf call is formed 706f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("onConnectionStateChanged: conn=" + conn); 70722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mState != State.ACTIVE) { 7080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(conn.getState()); 7090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void onConnectionEnded(SipConnection conn) { 7130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // set state to DISCONNECTED only when all conns are disconnected 714f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("onConnectionEnded: conn=" + conn); 71522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mState != State.DISCONNECTED) { 7160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean allConnectionsDisconnected = true; 717f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("---check connections: " 71822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville + mConnections.size()); 71922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) { 720f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log(" state=" + c.getState() + ": " 7210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + c); 7220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (c.getState() != State.DISCONNECTED) { 7230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville allConnectionsDisconnected = false; 7240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 7250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (allConnectionsDisconnected) setState(State.DISCONNECTED); 7280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville notifyDisconnectP(conn); 7300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private AudioGroup getAudioGroup() { 73322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mConnections.isEmpty()) return null; 73422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return ((SipConnection) mConnections.get(0)).getAudioGroup(); 7350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 736f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 737f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void log(String s) { 738f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.d(SC_TAG, s); 739f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 7400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private class SipConnection extends SipConnectionBase { 743f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final String SCN_TAG = "SipConnection"; 744f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final boolean SCN_DBG = true; 745f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 7460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipCall mOwner; 7470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipAudioCall mSipAudioCall; 7480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private Call.State mState = Call.State.IDLE; 7490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipProfile mPeer; 7500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private boolean mIncoming = false; 751f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private String mOriginalNumber; // may be a PSTN number 7520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipAudioCallAdapter mAdapter = new SipAudioCallAdapter() { 7540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 755b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen protected void onCallEnded(int cause) { 7560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (getDisconnectCause() != DisconnectCause.LOCAL) { 7570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setDisconnectCause(cause); 7580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 7600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.DISCONNECTED); 7610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipAudioCall sipAudioCall = mSipAudioCall; 762f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // FIXME: This goes null and is synchronized, but many uses aren't sync'd 7630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall = null; 7640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String sessionState = (sipAudioCall == null) 7650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ? "" 7660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : (sipAudioCall.getState() + ", "); 767f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("[SipAudioCallAdapter] onCallEnded: " 768b4350195add6b77d9352a41e1a8e5580a6a4816cSantos Cordon + hidePii(mPeer.getUriString()) + ": " + sessionState 7690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + "cause: " + getDisconnectCause() + ", on phone " 7700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + getPhone()); 7710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (sipAudioCall != null) { 7720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.setListener(null); 7730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.close(); 7740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOwner.onConnectionEnded(SipConnection.this); 7760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 7800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onCallEstablished(SipAudioCall call) { 7810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onChanged(call); 782f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // Race onChanged synchronized this isn't 7830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mState == Call.State.ACTIVE) call.startAudio(); 7840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 7870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onCallHeld(SipAudioCall call) { 7880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onChanged(call); 789f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // Race onChanged synchronized this isn't 7900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mState == Call.State.HOLDING) call.startAudio(); 7910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 7940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onChanged(SipAudioCall call) { 7950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 7960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Call.State newState = getCallStateFrom(call); 7970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mState == newState) return; 7980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (newState == Call.State.INCOMING) { 7990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(mOwner.getState()); // INCOMING or WAITING 8000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 80122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mOwner == mRingingCall) { 80222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mRingingCall.getState() == Call.State.WAITING) { 8030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 8040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switchHoldingAndActive(); 8050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (CallStateException e) { 8060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // disconnect the call. 8070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onCallEnded(DisconnectCause.LOCAL); 8080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return; 8090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 81122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.switchWith(mRingingCall); 8120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(newState); 8140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOwner.onConnectionStateChanged(SipConnection.this); 816f0e497e8cf789cf47bd60a68ac12e5e22dbbe855Shunta Sakai if (SCN_DBG) { 817f0e497e8cf789cf47bd60a68ac12e5e22dbbe855Shunta Sakai log("onChanged: " + hidePii(mPeer.getUriString()) + ": " + mState 818f0e497e8cf789cf47bd60a68ac12e5e22dbbe855Shunta Sakai + " on phone " + getPhone()); 819f0e497e8cf789cf47bd60a68ac12e5e22dbbe855Shunta Sakai } 8200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 824b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen protected void onError(int cause) { 825f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("onError: " + cause); 8260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onCallEnded(cause); 8270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville }; 8290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public SipConnection(SipCall owner, SipProfile callee, 8310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String originalNumber) { 8320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville super(originalNumber); 8330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOwner = owner; 8340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mPeer = callee; 8350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOriginalNumber = originalNumber; 8360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public SipConnection(SipCall owner, SipProfile callee) { 8390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville this(owner, callee, getUriString(callee)); 8400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 8430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public String getCnapName() { 8440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String displayName = mPeer.getDisplayName(); 8450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return TextUtils.isEmpty(displayName) ? null 8460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : displayName; 8470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 8500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public int getNumberPresentation() { 8510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return PhoneConstants.PRESENTATION_ALLOWED; 8520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void initIncomingCall(SipAudioCall sipAudioCall, Call.State newState) { 8550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(newState); 8560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall = sipAudioCall; 8570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.setListener(mAdapter); // call back to set state 8580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mIncoming = true; 8590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void acceptCall() throws CallStateException { 8620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 8630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.answerCall(TIMEOUT_ANSWER_CALL); 8640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 8650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("acceptCall(): " + e); 8660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void changeOwner(SipCall owner) { 8700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOwner = owner; 8710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup getAudioGroup() { 8740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mSipAudioCall == null) return null; 8750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mSipAudioCall.getAudioGroup(); 8760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void dial() throws SipException { 8790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.DIALING); 8800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall = mSipManager.makeAudioCall(mProfile, mPeer, null, 8810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville TIMEOUT_MAKE_CALL); 8820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.setListener(mAdapter); 8830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void hold() throws CallStateException { 8860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.HOLDING); 8870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 8880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.holdCall(TIMEOUT_HOLD_CALL); 8890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 8900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("hold(): " + e); 8910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void unhold(AudioGroup audioGroup) throws CallStateException { 8950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.setAudioGroup(audioGroup); 8960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.ACTIVE); 8970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 8980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.continueCall(TIMEOUT_HOLD_CALL); 8990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 9000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("unhold(): " + e); 9010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void setMute(boolean muted) { 9050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if ((mSipAudioCall != null) && (muted != mSipAudioCall.isMuted())) { 906f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("setState: prev muted=" + !muted + " new muted=" + muted); 9070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.toggleMute(); 9080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean getMute() { 9120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return (mSipAudioCall == null) ? false 9130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : mSipAudioCall.isMuted(); 9140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected void setState(Call.State state) { 9180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (state == mState) return; 9190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville super.setState(state); 9200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mState = state; 9210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Call.State getState() { 9250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mState; 9260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean isIncoming() { 9300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mIncoming; 9310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public String getAddress() { 9350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Phone app uses this to query caller ID. Return the original dial 9360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // number (which may be a PSTN number) instead of the peer's SIP 9370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // URI. 9380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mOriginalNumber; 9390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public SipCall getCall() { 9430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mOwner; 9440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected Phone getPhone() { 9480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mOwner.getPhone(); 9490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void hangup() throws CallStateException { 9530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 954f0e497e8cf789cf47bd60a68ac12e5e22dbbe855Shunta Sakai if (SCN_DBG) { 955f0e497e8cf789cf47bd60a68ac12e5e22dbbe855Shunta Sakai log("hangup: conn=" + hidePii(mPeer.getUriString()) 956f0e497e8cf789cf47bd60a68ac12e5e22dbbe855Shunta Sakai + ": " + mState + ": on phone " 957f0e497e8cf789cf47bd60a68ac12e5e22dbbe855Shunta Sakai + getPhone().getPhoneName()); 958f0e497e8cf789cf47bd60a68ac12e5e22dbbe855Shunta Sakai } 9590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!mState.isAlive()) return; 9600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 9610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipAudioCall sipAudioCall = mSipAudioCall; 9620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (sipAudioCall != null) { 9630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.setListener(null); 9640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.endCall(); 9650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 9670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("hangup(): " + e); 9680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } finally { 9690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mAdapter.onCallEnded(((mState == Call.State.INCOMING) 9700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville || (mState == Call.State.WAITING)) 9710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ? DisconnectCause.INCOMING_REJECTED 9720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : DisconnectCause.LOCAL); 9730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void separate() throws CallStateException { 9790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 9800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipCall call = (getPhone() == SipPhone.this) 98122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville ? (SipCall) getBackgroundCall() 98222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville : (SipCall) getForegroundCall(); 9830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (call.getState() != Call.State.IDLE) { 9840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException( 9850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville "cannot put conn back to a call in non-idle state: " 9860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + call.getState()); 9870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 988f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("separate: conn=" 9890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + mPeer.getUriString() + " from " + mOwner + " back to " 9900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + call); 9910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // separate the AudioGroup and connection from the original call 9930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Phone originalPhone = getPhone(); 9940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = call.getAudioGroup(); // may be null 9950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville call.add(this); 9960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.setAudioGroup(audioGroup); 9970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // put the original call to bg; and the separated call becomes 9990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // fg if it was in bg 10000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville originalPhone.switchHoldingAndActive(); 10010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 10020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // start audio and notify the phone app of the state change 100322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville call = (SipCall) getForegroundCall(); 10040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.startAudio(); 10050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville call.onConnectionStateChanged(this); 10060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 10070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 10080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1009f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void log(String s) { 1010f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.d(SCN_TAG, s); 10110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 10120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 10130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 10140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private abstract class SipAudioCallAdapter extends SipAudioCall.Listener { 1015f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final String SACA_TAG = "SipAudioCallAdapter"; 1016f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final boolean SACA_DBG = true; 1017b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen /** Call ended with cause defined in {@link DisconnectCause}. */ 1018b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen protected abstract void onCallEnded(int cause); 1019b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen /** Call failed with cause defined in {@link DisconnectCause}. */ 1020b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen protected abstract void onError(int cause); 10210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 10220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 10230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onCallEnded(SipAudioCall call) { 1024f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SACA_DBG) log("onCallEnded: call=" + call); 10250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onCallEnded(call.isInCall() 1026b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen ? DisconnectCause.NORMAL 1027b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen : DisconnectCause.INCOMING_MISSED); 10280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 10290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 10300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 10310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onCallBusy(SipAudioCall call) { 1032f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SACA_DBG) log("onCallBusy: call=" + call); 1033b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onCallEnded(DisconnectCause.BUSY); 10340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 10350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 10360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 10370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onError(SipAudioCall call, int errorCode, 10380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String errorMessage) { 1039f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SACA_DBG) { 1040f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville log("onError: call=" + call + " code="+ SipErrorCode.toString(errorCode) 1041f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville + ": " + errorMessage); 1042f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 10430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch (errorCode) { 10440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.SERVER_UNREACHABLE: 1045b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.SERVER_UNREACHABLE); 10460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.PEER_NOT_REACHABLE: 1048b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.NUMBER_UNREACHABLE); 10490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.INVALID_REMOTE_URI: 1051b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.INVALID_NUMBER); 10520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.TIME_OUT: 10540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.TRANSACTION_TERMINTED: 1055b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.TIMED_OUT); 10560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.DATA_CONNECTION_LOST: 1058b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.LOST_SIGNAL); 10590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.INVALID_CREDENTIALS: 1061b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.INVALID_CREDENTIALS); 10620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.CROSS_DOMAIN_AUTHENTICATION: 1064b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.OUT_OF_NETWORK); 10650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.SERVER_ERROR: 1067b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.SERVER_ERROR); 10680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.SOCKET_ERROR: 10700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.CLIENT_ERROR: 10710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville default: 1072b7b7a62112b79571adf74372c5f5366fd62d0031Anders Kristensen onError(DisconnectCause.ERROR_UNSPECIFIED); 10730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 10740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1075f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 1076f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void log(String s) { 1077f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.d(SACA_TAG, s); 1078f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 10790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1080b4350195add6b77d9352a41e1a8e5580a6a4816cSantos Cordon 1081b4350195add6b77d9352a41e1a8e5580a6a4816cSantos Cordon public static String hidePii(String s) { 1082f0e497e8cf789cf47bd60a68ac12e5e22dbbe855Shunta Sakai return VDBG ? Rlog.pii(LOG_TAG, s) : "xxxxx"; 1083b4350195add6b77d9352a41e1a8e5580a6a4816cSantos Cordon } 10840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville} 1085