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; 300825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.telephony.PhoneNumberUtils; 310825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.telephony.ServiceState; 320825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.text.TextUtils; 3399c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Savilleimport android.telephony.Rlog; 340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 350825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.Call; 360825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.CallStateException; 370825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.Connection; 380825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.Phone; 390825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.PhoneConstants; 400825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.PhoneNotifier; 410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 420825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.text.ParseException; 430825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.List; 440825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.regex.Pattern; 450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/** 470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * {@hide} 480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 490825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillepublic class SipPhone extends SipPhoneBase { 500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String LOG_TAG = "SipPhone"; 51f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final boolean DBG = true; 52ff4e317d24f0d23bdc0f306d53ddc51f2f1ecf6aWink Saville private static final boolean VDBG = false; // STOPSHIP if true 530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int TIMEOUT_MAKE_CALL = 15; // in seconds 540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int TIMEOUT_ANSWER_CALL = 8; // in seconds 550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int TIMEOUT_HOLD_CALL = 15; // in seconds 560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // A call that is ringing or (call) waiting 5822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville private SipCall mRingingCall = new SipCall(); 5922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville private SipCall mForegroundCall = new SipCall(); 6022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville private SipCall mBackgroundCall = new SipCall(); 610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipManager mSipManager; 630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipProfile mProfile; 640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipPhone (Context context, PhoneNotifier notifier, SipProfile profile) { 66ff4e317d24f0d23bdc0f306d53ddc51f2f1ecf6aWink Saville super("SIP:" + profile.getUriString(), context, notifier); 670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 68f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("new SipPhone: " + profile.getUriString()); 6922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall = new SipCall(); 7022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall = new SipCall(); 7122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mBackgroundCall = new SipCall(); 720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mProfile = profile; 730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipManager = SipManager.newInstance(context); 740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean equals(Object o) { 780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (o == this) return true; 790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!(o instanceof SipPhone)) return false; 800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipPhone that = (SipPhone) o; 810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mProfile.getUriString().equals(that.mProfile.getUriString()); 820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public String getSipUri() { 850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mProfile.getUriString(); 860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean equals(SipPhone phone) { 890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return getSipUri().equals(phone.getSipUri()); 900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean canTake(Object incomingCall) { 93f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // FIXME: Is synchronizing on the class necessary, should we use a mLockObj? 94f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // Also there are many things not synchronized, of course 95f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // this may be true of CdmaPhone and GsmPhone too!!! 960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 97f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (!(incomingCall instanceof SipAudioCall)) { 98f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("canTake: ret=false, not a SipAudioCall"); 99f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return false; 100f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 10122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mRingingCall.getState().isAlive()) { 102f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("canTake: ret=false, ringingCall not alive"); 103f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return false; 104f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 1050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: is it true that we cannot take any incoming call if 1070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // both foreground and background are active 10822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mForegroundCall.getState().isAlive() 10922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville && mBackgroundCall.getState().isAlive()) { 110f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) { 111f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville log("canTake: ret=false," + 112f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville " foreground and background both alive"); 113f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 1140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return false; 1150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 1180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipAudioCall sipAudioCall = (SipAudioCall) incomingCall; 119f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("canTake: taking call from: " 1200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + sipAudioCall.getPeerProfile().getUriString()); 1210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String localUri = sipAudioCall.getLocalProfile().getUriString(); 1220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (localUri.equals(mProfile.getUriString())) { 12322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville boolean makeCallWait = mForegroundCall.getState().isAlive(); 12422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.initIncomingCall(sipAudioCall, makeCallWait); 1250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (sipAudioCall.getState() 1260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville != SipSession.State.INCOMING_CALL) { 1270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Peer cancelled the call! 128f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log(" canTake: call cancelled !!"); 12922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.reset(); 1300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return true; 1320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (Exception e) { 1340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Peer may cancel the call at any time during the time we hook 1350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // up ringingCall with sipAudioCall. Clean up ringingCall when 1360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // that happens. 137f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log(" canTake: exception e=" + e); 13822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.reset(); 1390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 140f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("canTake: NOT taking !!"); 1410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return false; 1420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 145f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 1460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void acceptCall() throws CallStateException { 1470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 14822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if ((mRingingCall.getState() == Call.State.INCOMING) || 14922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville (mRingingCall.getState() == Call.State.WAITING)) { 150f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("acceptCall: accepting"); 1510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Always unmute when answering a new call 15222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.setMute(false); 15322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.acceptCall(); 1540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 155f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) { 156f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville log("acceptCall:" + 157f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville " throw CallStateException(\"phone not ringing\")"); 158f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 1590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("phone not ringing"); 1600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 164f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 1650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void rejectCall() throws CallStateException { 1660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 16722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mRingingCall.getState().isRinging()) { 168f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("rejectCall: rejecting"); 16922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.rejectCall(); 1700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 171f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) { 172f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville log("rejectCall:" + 173f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville " throw CallStateException(\"phone not ringing\")"); 174f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 1750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("phone not ringing"); 1760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 180f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 1810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Connection dial(String dialString) throws CallStateException { 1820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 1830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return dialInternal(dialString); 1840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private Connection dialInternal(String dialString) 1880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throws CallStateException { 189f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("dialInternal: dialString=" + (VDBG ? dialString : "xxxxxx")); 1900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville clearDisconnected(); 1910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!canDial()) { 193f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville throw new CallStateException("dialInternal: cannot dial in current state"); 1940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 19522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mForegroundCall.getState() == SipCall.State.ACTIVE) { 1960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switchHoldingAndActive(); 1970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 19822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mForegroundCall.getState() != SipCall.State.IDLE) { 1990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville //we should have failed in !canDial() above before we get here 2000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("cannot dial in current state"); 2010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 20322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.setMute(false); 2040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 20522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville Connection c = mForegroundCall.dial(dialString); 2060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return c; 2070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 208f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville loge("dialInternal: ", e); 2090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("dial error: " + e); 2100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 213f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void switchHoldingAndActive() throws CallStateException { 215f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("dialInternal: switch fg and bg"); 2160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 21722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.switchWith(mBackgroundCall); 21822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mBackgroundCall.getState().isAlive()) mBackgroundCall.hold(); 21922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mForegroundCall.getState().isAlive()) mForegroundCall.unhold(); 2200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 223f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean canConference() { 225f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("canConference: ret=true"); 2260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return true; 2270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 229f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void conference() throws CallStateException { 2310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 23222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if ((mForegroundCall.getState() != SipCall.State.ACTIVE) 23322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville || (mForegroundCall.getState() != SipCall.State.ACTIVE)) { 2340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("wrong state to merge calls: fg=" 23522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville + mForegroundCall.getState() + ", bg=" 23622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville + mBackgroundCall.getState()); 2370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 238f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (DBG) log("conference: merge fg & bg"); 23922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.merge(mBackgroundCall); 2400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void conference(Call that) throws CallStateException { 2440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 2450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!(that instanceof SipCall)) { 2460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("expect " + SipCall.class 2470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ", cannot merge with " + that.getClass()); 2480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 24922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.merge((SipCall) that); 2500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 253f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean canTransfer() { 2550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return false; 2560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 258f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 259cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville public void explicitCallTransfer() { 2600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville //mCT.explicitCallTransfer(); 2610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 263f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void clearDisconnected() { 2650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 26622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mRingingCall.clearDisconnected(); 26722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.clearDisconnected(); 26822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mBackgroundCall.clearDisconnected(); 2690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville updatePhoneState(); 2710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville notifyPreciseCallStateChanged(); 2720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 275f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void sendDtmf(char c) { 2770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!PhoneNumberUtils.is12Key(c)) { 278f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville loge("sendDtmf called with invalid character '" + c + "'"); 27922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville } else if (mForegroundCall.getState().isAlive()) { 2800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 28122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.sendDtmf(c); 2820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 286f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void startDtmf(char c) { 2880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!PhoneNumberUtils.is12Key(c)) { 289f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville loge("startDtmf called with invalid character '" + c + "'"); 2900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 2910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sendDtmf(c); 2920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 295f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 2960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void stopDtmf() { 2970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // no op 2980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void sendBurstDtmf(String dtmfString) { 301f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville loge("sendBurstDtmf() is a CDMA method"); 3020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 304f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void getOutgoingCallerIdDisplay(Message onComplete) { 3060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: what to reply? 3070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AsyncResult.forMessage(onComplete, null, null); 3080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onComplete.sendToTarget(); 3090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 311f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, 3130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Message onComplete) { 3140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: what's this for SIP? 3150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AsyncResult.forMessage(onComplete, null, null); 3160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onComplete.sendToTarget(); 3170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 319f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void getCallWaiting(Message onComplete) { 3210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: what to reply? 3220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AsyncResult.forMessage(onComplete, null, null); 3230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onComplete.sendToTarget(); 3240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 326f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void setCallWaiting(boolean enable, Message onComplete) { 3280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: what to reply? 329f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville loge("call waiting not supported"); 3300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 3330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void setEchoSuppressionEnabled(boolean enabled) { 3340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // TODO: Remove the enabled argument. We should check the speakerphone 3350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // state with AudioManager instead of keeping a state here so the 3360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // method with a state argument is redundant. Also rename the method 3370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // to something like onSpeaerphoneStateChanged(). Echo suppression may 3380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // not be available on every device. 3390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 34022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.setAudioGroupMode(); 3410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 344f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void setMute(boolean muted) { 3460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 34722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.setMute(muted); 3480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 351f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean getMute() { 35322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return (mForegroundCall.getState().isAlive() 35422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville ? mForegroundCall.getMute() 35522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville : mBackgroundCall.getMute()); 3560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 358f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Call getForegroundCall() { 36022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return mForegroundCall; 3610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 363f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Call getBackgroundCall() { 36522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return mBackgroundCall; 3660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 368f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Call getRingingCall() { 37022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return mRingingCall; 3710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 373f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville @Override 3740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public ServiceState getServiceState() { 3750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: we may need to provide this when data connectivity is lost 3760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // or when server is down 3770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return super.getServiceState(); 3780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private String getUriString(SipProfile p) { 3810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // SipProfile.getUriString() may contain "SIP:" and port 3820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return p.getUserName() + "@" + getSipDomain(p); 3830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private String getSipDomain(SipProfile p) { 3860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String domain = p.getSipDomain(); 3870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // TODO: move this to SipProfile 3880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (domain.endsWith(":5060")) { 3890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return domain.substring(0, domain.length() - 5); 3900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 3910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return domain; 3920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 395f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static Call.State getCallStateFrom(SipAudioCall sipAudioCall) { 396f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (sipAudioCall.isOnHold()) return Call.State.HOLDING; 397f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville int sessionState = sipAudioCall.getState(); 398f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville switch (sessionState) { 399f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.READY_TO_CALL: return Call.State.IDLE; 400f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.INCOMING_CALL: 401f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.INCOMING_CALL_ANSWERING: return Call.State.INCOMING; 402f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.OUTGOING_CALL: return Call.State.DIALING; 403f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.OUTGOING_CALL_RING_BACK: return Call.State.ALERTING; 404f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.OUTGOING_CALL_CANCELING: return Call.State.DISCONNECTING; 405f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville case SipSession.State.IN_CALL: return Call.State.ACTIVE; 406f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville default: 407f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville slog("illegal connection state: " + sessionState); 408f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return Call.State.DISCONNECTED; 409f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 410f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 411f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 412f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void log(String s) { 413f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.d(LOG_TAG, s); 414f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 415f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 416f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static void slog(String s) { 417f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.d(LOG_TAG, s); 418f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 419f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 420f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void loge(String s) { 421f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.e(LOG_TAG, s); 422f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 423f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 424f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void loge(String s, Exception e) { 425f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.e(LOG_TAG, s, e); 426f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 427f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 4280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private class SipCall extends SipCallBase { 429f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final String SC_TAG = "SipCall"; 430f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final boolean SC_DBG = true; 431f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final boolean SC_VDBG = false; // STOPSHIP if true 432f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 4330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void reset() { 434f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("reset"); 43522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mConnections.clear(); 4360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.IDLE); 4370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void switchWith(SipCall that) { 440f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("switchWith"); 4410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 4420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipCall tmp = new SipCall(); 4430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville tmp.takeOver(this); 4440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville this.takeOver(that); 4450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville that.takeOver(tmp); 4460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void takeOver(SipCall that) { 450f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("takeOver"); 45122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mConnections = that.mConnections; 45222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mState = that.mState; 45322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) { 4540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ((SipConnection) c).changeOwner(this); 4550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 4590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Phone getPhone() { 4600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return SipPhone.this; 4610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 4640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public List<Connection> getConnections() { 465f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_VDBG) log("getConnections"); 4660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 4670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME should return Collections.unmodifiableList(); 46822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return mConnections; 4690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Connection dial(String originalNumber) throws SipException { 473f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("dial: num=" + (SC_VDBG ? originalNumber : "xxx")); 474f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // TODO: Should this be synchronized? 4750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String calleeSipUri = originalNumber; 4760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!calleeSipUri.contains("@")) { 4770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String replaceStr = Pattern.quote(mProfile.getUserName() + "@"); 4780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville calleeSipUri = mProfile.getUriString().replaceFirst(replaceStr, 4790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville calleeSipUri + "@"); 4800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 4820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipProfile callee = 4830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville new SipProfile.Builder(calleeSipUri).build(); 4840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipConnection c = new SipConnection(this, callee, 4850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville originalNumber); 4860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville c.dial(); 48722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mConnections.add(c); 4880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.DIALING); 4890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return c; 4900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (ParseException e) { 4910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new SipException("dial", e); 4920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 4960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void hangup() throws CallStateException { 4970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 49822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mState.isAlive()) { 499f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("hangup: call " + getState() 5000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ": " + this + " on phone " + getPhone()); 5010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(State.DISCONNECTING); 5020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville CallStateException excp = null; 50322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) { 5040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 5050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville c.hangup(); 5060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (CallStateException e) { 5070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville excp = e; 5080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (excp != null) throw excp; 5110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 512f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("hangup: dead call " + getState() 5130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ": " + this + " on phone " + getPhone()); 5140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void initIncomingCall(SipAudioCall sipAudioCall, boolean makeCallWait) { 5190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipProfile callee = sipAudioCall.getPeerProfile(); 5200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipConnection c = new SipConnection(this, callee); 52122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mConnections.add(c); 5220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Call.State newState = makeCallWait ? State.WAITING : State.INCOMING; 5240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville c.initIncomingCall(sipAudioCall, newState); 5250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(newState); 5270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville notifyNewRingingConnectionP(c); 5280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void rejectCall() throws CallStateException { 531f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("rejectCall:"); 5320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville hangup(); 5330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void acceptCall() throws CallStateException { 536f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("acceptCall: accepting"); 53722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (this != mRingingCall) { 5380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("acceptCall() in a non-ringing call"); 5390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 54022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mConnections.size() != 1) { 5410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("acceptCall() in a conf call"); 5420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 54322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville ((SipConnection) mConnections.get(0)).acceptCall(); 5440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private boolean isSpeakerOn() { 547f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Boolean ret = ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)) 5480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville .isSpeakerphoneOn(); 549f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_VDBG) log("isSpeakerOn: ret=" + ret); 550f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return ret; 5510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void setAudioGroupMode() { 5540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = getAudioGroup(); 555f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (audioGroup == null) { 556f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("setAudioGroupMode: audioGroup == null ignore"); 557f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return; 558f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 5590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int mode = audioGroup.getMode(); 56022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mState == State.HOLDING) { 5610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.setMode(AudioGroup.MODE_ON_HOLD); 5620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else if (getMute()) { 5630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.setMode(AudioGroup.MODE_MUTED); 5640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else if (isSpeakerOn()) { 5650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.setMode(AudioGroup.MODE_ECHO_SUPPRESSION); 5660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 5670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.setMode(AudioGroup.MODE_NORMAL); 5680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 569f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log(String.format( 570f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville "setAudioGroupMode change: %d --> %d", mode, 5710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.getMode())); 5720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void hold() throws CallStateException { 575f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("hold:"); 5760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(State.HOLDING); 57722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) ((SipConnection) c).hold(); 5780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setAudioGroupMode(); 5790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void unhold() throws CallStateException { 582f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("unhold:"); 5830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(State.ACTIVE); 5840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = new AudioGroup(); 58522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) { 5860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ((SipConnection) c).unhold(audioGroup); 5870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setAudioGroupMode(); 5890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void setMute(boolean muted) { 592f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("setMute: muted=" + muted); 59322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) { 5940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ((SipConnection) c).setMute(muted); 5950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean getMute() { 59922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville boolean ret = mConnections.isEmpty() 6000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ? false 60122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville : ((SipConnection) mConnections.get(0)).getMute(); 602f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("getMute: ret=" + ret); 603f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return ret; 6040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void merge(SipCall that) throws CallStateException { 607f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("merge:"); 6080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = getAudioGroup(); 6090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // copy to an array to avoid concurrent modification as connections 6110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // in that.connections will be removed in add(SipConnection). 61222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville Connection[] cc = that.mConnections.toArray( 61322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville new Connection[that.mConnections.size()]); 6140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (Connection c : cc) { 6150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipConnection conn = (SipConnection) c; 6160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville add(conn); 6170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (conn.getState() == Call.State.HOLDING) { 6180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville conn.unhold(audioGroup); 6190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville that.setState(Call.State.IDLE); 6220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void add(SipConnection conn) { 625f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("add:"); 6260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipCall call = conn.getCall(); 6270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (call == this) return; 62822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (call != null) call.mConnections.remove(conn); 6290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 63022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mConnections.add(conn); 6310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville conn.changeOwner(this); 6320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void sendDtmf(char c) { 635f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("sendDtmf: c=" + c); 6360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = getAudioGroup(); 637f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (audioGroup == null) { 638f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("sendDtmf: audioGroup == null, ignore c=" + c); 639f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville return; 640f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 6410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.sendDtmf(convertDtmf(c)); 6420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private int convertDtmf(char c) { 6450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int code = c - '0'; 6460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if ((code < 0) || (code > 9)) { 6470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch (c) { 6480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case '*': return 10; 6490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case '#': return 11; 6500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 'A': return 12; 6510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 'B': return 13; 6520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 'C': return 14; 6530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 'D': return 15; 6540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville default: 6550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new IllegalArgumentException( 6560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville "invalid DTMF char: " + (int) c); 6570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return code; 6600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 6630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected void setState(State newState) { 66422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mState != newState) { 66522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (SC_DBG) log("setState: cur state" + mState 6660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + " --> " + newState + ": " + this + ": on phone " 66722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville + getPhone() + " " + mConnections.size()); 6680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (newState == Call.State.ALERTING) { 67022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mState = newState; // need in ALERTING to enable ringback 67122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville startRingbackTone(); 67222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville } else if (mState == Call.State.ALERTING) { 67322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville stopRingbackTone(); 6740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 67522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mState = newState; 6760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville updatePhoneState(); 6770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville notifyPreciseCallStateChanged(); 6780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void onConnectionStateChanged(SipConnection conn) { 6820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // this can be called back when a conf call is formed 683f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("onConnectionStateChanged: conn=" + conn); 68422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mState != State.ACTIVE) { 6850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(conn.getState()); 6860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void onConnectionEnded(SipConnection conn) { 6900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // set state to DISCONNECTED only when all conns are disconnected 691f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("onConnectionEnded: conn=" + conn); 69222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mState != State.DISCONNECTED) { 6930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean allConnectionsDisconnected = true; 694f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log("---check connections: " 69522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville + mConnections.size()); 69622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville for (Connection c : mConnections) { 697f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SC_DBG) log(" state=" + c.getState() + ": " 6980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + c); 6990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (c.getState() != State.DISCONNECTED) { 7000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville allConnectionsDisconnected = false; 7010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 7020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (allConnectionsDisconnected) setState(State.DISCONNECTED); 7050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville notifyDisconnectP(conn); 7070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private AudioGroup getAudioGroup() { 71022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mConnections.isEmpty()) return null; 71122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville return ((SipConnection) mConnections.get(0)).getAudioGroup(); 7120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 713f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 714f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void log(String s) { 715f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.d(SC_TAG, s); 716f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 7170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private class SipConnection extends SipConnectionBase { 720f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final String SCN_TAG = "SipConnection"; 721f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final boolean SCN_DBG = true; 722f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 7230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipCall mOwner; 7240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipAudioCall mSipAudioCall; 7250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private Call.State mState = Call.State.IDLE; 7260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipProfile mPeer; 7270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private boolean mIncoming = false; 728f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private String mOriginalNumber; // may be a PSTN number 7290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipAudioCallAdapter mAdapter = new SipAudioCallAdapter() { 7310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 7320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected void onCallEnded(DisconnectCause cause) { 7330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (getDisconnectCause() != DisconnectCause.LOCAL) { 7340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setDisconnectCause(cause); 7350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 7370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.DISCONNECTED); 7380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipAudioCall sipAudioCall = mSipAudioCall; 739f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // FIXME: This goes null and is synchronized, but many uses aren't sync'd 7400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall = null; 7410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String sessionState = (sipAudioCall == null) 7420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ? "" 7430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : (sipAudioCall.getState() + ", "); 744f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("[SipAudioCallAdapter] onCallEnded: " 7450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + mPeer.getUriString() + ": " + sessionState 7460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + "cause: " + getDisconnectCause() + ", on phone " 7470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + getPhone()); 7480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (sipAudioCall != null) { 7490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.setListener(null); 7500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.close(); 7510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOwner.onConnectionEnded(SipConnection.this); 7530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 7570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onCallEstablished(SipAudioCall call) { 7580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onChanged(call); 759f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // Race onChanged synchronized this isn't 7600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mState == Call.State.ACTIVE) call.startAudio(); 7610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 7640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onCallHeld(SipAudioCall call) { 7650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onChanged(call); 766f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville // Race onChanged synchronized this isn't 7670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mState == Call.State.HOLDING) call.startAudio(); 7680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 7710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onChanged(SipAudioCall call) { 7720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 7730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Call.State newState = getCallStateFrom(call); 7740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mState == newState) return; 7750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (newState == Call.State.INCOMING) { 7760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(mOwner.getState()); // INCOMING or WAITING 7770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 77822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mOwner == mRingingCall) { 77922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville if (mRingingCall.getState() == Call.State.WAITING) { 7800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 7810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switchHoldingAndActive(); 7820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (CallStateException e) { 7830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // disconnect the call. 7840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onCallEnded(DisconnectCause.LOCAL); 7850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return; 7860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 78822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville mForegroundCall.switchWith(mRingingCall); 7890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(newState); 7910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOwner.onConnectionStateChanged(SipConnection.this); 793f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("onChanged: " 7940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + mPeer.getUriString() + ": " + mState 7950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + " on phone " + getPhone()); 7960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 8000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected void onError(DisconnectCause cause) { 801f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("onError: " + cause); 8020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onCallEnded(cause); 8030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville }; 8050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public SipConnection(SipCall owner, SipProfile callee, 8070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String originalNumber) { 8080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville super(originalNumber); 8090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOwner = owner; 8100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mPeer = callee; 8110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOriginalNumber = originalNumber; 8120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public SipConnection(SipCall owner, SipProfile callee) { 8150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville this(owner, callee, getUriString(callee)); 8160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 8190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public String getCnapName() { 8200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String displayName = mPeer.getDisplayName(); 8210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return TextUtils.isEmpty(displayName) ? null 8220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : displayName; 8230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 8260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public int getNumberPresentation() { 8270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return PhoneConstants.PRESENTATION_ALLOWED; 8280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void initIncomingCall(SipAudioCall sipAudioCall, Call.State newState) { 8310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(newState); 8320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall = sipAudioCall; 8330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.setListener(mAdapter); // call back to set state 8340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mIncoming = true; 8350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void acceptCall() throws CallStateException { 8380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 8390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.answerCall(TIMEOUT_ANSWER_CALL); 8400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 8410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("acceptCall(): " + e); 8420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void changeOwner(SipCall owner) { 8460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOwner = owner; 8470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup getAudioGroup() { 8500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mSipAudioCall == null) return null; 8510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mSipAudioCall.getAudioGroup(); 8520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void dial() throws SipException { 8550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.DIALING); 8560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall = mSipManager.makeAudioCall(mProfile, mPeer, null, 8570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville TIMEOUT_MAKE_CALL); 8580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.setListener(mAdapter); 8590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void hold() throws CallStateException { 8620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.HOLDING); 8630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 8640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.holdCall(TIMEOUT_HOLD_CALL); 8650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 8660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("hold(): " + e); 8670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void unhold(AudioGroup audioGroup) throws CallStateException { 8710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.setAudioGroup(audioGroup); 8720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.ACTIVE); 8730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 8740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.continueCall(TIMEOUT_HOLD_CALL); 8750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 8760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("unhold(): " + e); 8770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void setMute(boolean muted) { 8810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if ((mSipAudioCall != null) && (muted != mSipAudioCall.isMuted())) { 882f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("setState: prev muted=" + !muted + " new muted=" + muted); 8830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.toggleMute(); 8840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean getMute() { 8880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return (mSipAudioCall == null) ? false 8890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : mSipAudioCall.isMuted(); 8900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 8930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected void setState(Call.State state) { 8940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (state == mState) return; 8950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville super.setState(state); 8960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mState = state; 8970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Call.State getState() { 9010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mState; 9020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean isIncoming() { 9060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mIncoming; 9070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public String getAddress() { 9110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Phone app uses this to query caller ID. Return the original dial 9120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // number (which may be a PSTN number) instead of the peer's SIP 9130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // URI. 9140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mOriginalNumber; 9150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public SipCall getCall() { 9190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mOwner; 9200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected Phone getPhone() { 9240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mOwner.getPhone(); 9250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void hangup() throws CallStateException { 9290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 930f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("hangup: conn=" + mPeer.getUriString() 9310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ": " + mState + ": on phone " 9320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + getPhone().getPhoneName()); 9330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!mState.isAlive()) return; 9340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 9350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipAudioCall sipAudioCall = mSipAudioCall; 9360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (sipAudioCall != null) { 9370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.setListener(null); 9380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.endCall(); 9390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 9410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("hangup(): " + e); 9420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } finally { 9430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mAdapter.onCallEnded(((mState == Call.State.INCOMING) 9440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville || (mState == Call.State.WAITING)) 9450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ? DisconnectCause.INCOMING_REJECTED 9460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : DisconnectCause.LOCAL); 9470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void separate() throws CallStateException { 9530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 9540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipCall call = (getPhone() == SipPhone.this) 95522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville ? (SipCall) getBackgroundCall() 95622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville : (SipCall) getForegroundCall(); 9570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (call.getState() != Call.State.IDLE) { 9580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException( 9590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville "cannot put conn back to a call in non-idle state: " 9600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + call.getState()); 9610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 962f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SCN_DBG) log("separate: conn=" 9630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + mPeer.getUriString() + " from " + mOwner + " back to " 9640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + call); 9650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // separate the AudioGroup and connection from the original call 9670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Phone originalPhone = getPhone(); 9680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = call.getAudioGroup(); // may be null 9690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville call.add(this); 9700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.setAudioGroup(audioGroup); 9710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // put the original call to bg; and the separated call becomes 9730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // fg if it was in bg 9740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville originalPhone.switchHoldingAndActive(); 9750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // start audio and notify the phone app of the state change 97722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville call = (SipCall) getForegroundCall(); 9780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.startAudio(); 9790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville call.onConnectionStateChanged(this); 9800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 983f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void log(String s) { 984f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.d(SCN_TAG, s); 9850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private abstract class SipAudioCallAdapter extends SipAudioCall.Listener { 989f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final String SACA_TAG = "SipAudioCallAdapter"; 990f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private static final boolean SACA_DBG = true; 9910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected abstract void onCallEnded(Connection.DisconnectCause cause); 9920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected abstract void onError(Connection.DisconnectCause cause); 9930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onCallEnded(SipAudioCall call) { 996f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SACA_DBG) log("onCallEnded: call=" + call); 9970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onCallEnded(call.isInCall() 9980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ? Connection.DisconnectCause.NORMAL 9990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : Connection.DisconnectCause.INCOMING_MISSED); 10000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 10010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 10020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 10030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onCallBusy(SipAudioCall call) { 1004f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SACA_DBG) log("onCallBusy: call=" + call); 10050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onCallEnded(Connection.DisconnectCause.BUSY); 10060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 10070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 10080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 10090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onError(SipAudioCall call, int errorCode, 10100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String errorMessage) { 1011f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville if (SACA_DBG) { 1012f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville log("onError: call=" + call + " code="+ SipErrorCode.toString(errorCode) 1013f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville + ": " + errorMessage); 1014f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 10150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch (errorCode) { 10160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.SERVER_UNREACHABLE: 10170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onError(Connection.DisconnectCause.SERVER_UNREACHABLE); 10180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.PEER_NOT_REACHABLE: 10200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onError(Connection.DisconnectCause.NUMBER_UNREACHABLE); 10210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.INVALID_REMOTE_URI: 10230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onError(Connection.DisconnectCause.INVALID_NUMBER); 10240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.TIME_OUT: 10260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.TRANSACTION_TERMINTED: 10270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onError(Connection.DisconnectCause.TIMED_OUT); 10280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.DATA_CONNECTION_LOST: 10300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onError(Connection.DisconnectCause.LOST_SIGNAL); 10310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.INVALID_CREDENTIALS: 10330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onError(Connection.DisconnectCause.INVALID_CREDENTIALS); 10340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.CROSS_DOMAIN_AUTHENTICATION: 10360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onError(Connection.DisconnectCause.OUT_OF_NETWORK); 10370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.SERVER_ERROR: 10390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onError(Connection.DisconnectCause.SERVER_ERROR); 10400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 10410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.SOCKET_ERROR: 10420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.CLIENT_ERROR: 10430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville default: 10440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onError(Connection.DisconnectCause.ERROR_UNSPECIFIED); 10450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 10460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1047f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville 1048f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville private void log(String s) { 1049f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville Rlog.d(SACA_TAG, s); 1050f1ac06f0498ec7cb7489c835bcd2eed568b5f6a6Wink Saville } 10510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 10520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville} 1053