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; 330825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.util.Log; 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"; 510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final boolean DEBUG = true; 520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int TIMEOUT_MAKE_CALL = 15; // in seconds 530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int TIMEOUT_ANSWER_CALL = 8; // in seconds 540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int TIMEOUT_HOLD_CALL = 15; // in seconds 550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // A call that is ringing or (call) waiting 570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipCall ringingCall = new SipCall(); 580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipCall foregroundCall = new SipCall(); 590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipCall backgroundCall = new SipCall(); 600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipManager mSipManager; 620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipProfile mProfile; 630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipPhone (Context context, PhoneNotifier notifier, SipProfile profile) { 650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville super(context, notifier); 660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (DEBUG) Log.d(LOG_TAG, "new SipPhone: " + profile.getUriString()); 680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ringingCall = new SipCall(); 690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville foregroundCall = new SipCall(); 700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville backgroundCall = new SipCall(); 710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mProfile = profile; 720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipManager = SipManager.newInstance(context); 730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean equals(Object o) { 770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (o == this) return true; 780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!(o instanceof SipPhone)) return false; 790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipPhone that = (SipPhone) o; 800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mProfile.getUriString().equals(that.mProfile.getUriString()); 810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public String getPhoneName() { 840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return "SIP:" + getUriString(mProfile); 850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public String getSipUri() { 880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mProfile.getUriString(); 890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean equals(SipPhone phone) { 920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return getSipUri().equals(phone.getSipUri()); 930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean canTake(Object incomingCall) { 960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!(incomingCall instanceof SipAudioCall)) return false; 980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (ringingCall.getState().isAlive()) return false; 990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: is it true that we cannot take any incoming call if 1010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // both foreground and background are active 1020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (foregroundCall.getState().isAlive() 1030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville && backgroundCall.getState().isAlive()) { 1040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return false; 1050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 1080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipAudioCall sipAudioCall = (SipAudioCall) incomingCall; 1090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (DEBUG) Log.d(LOG_TAG, "+++ taking call from: " 1100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + sipAudioCall.getPeerProfile().getUriString()); 1110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String localUri = sipAudioCall.getLocalProfile().getUriString(); 1120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (localUri.equals(mProfile.getUriString())) { 1130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean makeCallWait = foregroundCall.getState().isAlive(); 1140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ringingCall.initIncomingCall(sipAudioCall, makeCallWait); 1150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (sipAudioCall.getState() 1160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville != SipSession.State.INCOMING_CALL) { 1170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Peer cancelled the call! 1180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (DEBUG) Log.d(LOG_TAG, " call cancelled !!"); 1190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ringingCall.reset(); 1200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return true; 1220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (Exception e) { 1240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Peer may cancel the call at any time during the time we hook 1250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // up ringingCall with sipAudioCall. Clean up ringingCall when 1260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // that happens. 1270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ringingCall.reset(); 1280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return false; 1300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void acceptCall() throws CallStateException { 1340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 1350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if ((ringingCall.getState() == Call.State.INCOMING) || 1360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville (ringingCall.getState() == Call.State.WAITING)) { 1370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (DEBUG) Log.d(LOG_TAG, "acceptCall"); 1380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Always unmute when answering a new call 1390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ringingCall.setMute(false); 1400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ringingCall.acceptCall(); 1410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 1420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("phone not ringing"); 1430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void rejectCall() throws CallStateException { 1480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 1490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (ringingCall.getState().isRinging()) { 1500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (DEBUG) Log.d(LOG_TAG, "rejectCall"); 1510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ringingCall.rejectCall(); 1520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 1530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("phone not ringing"); 1540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Connection dial(String dialString) throws CallStateException { 1590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 1600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return dialInternal(dialString); 1610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private Connection dialInternal(String dialString) 1650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throws CallStateException { 1660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville clearDisconnected(); 1670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!canDial()) { 1690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("cannot dial in current state"); 1700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (foregroundCall.getState() == SipCall.State.ACTIVE) { 1720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switchHoldingAndActive(); 1730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (foregroundCall.getState() != SipCall.State.IDLE) { 1750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville //we should have failed in !canDial() above before we get here 1760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("cannot dial in current state"); 1770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville foregroundCall.setMute(false); 1800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 1810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Connection c = foregroundCall.dial(dialString); 1820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return c; 1830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 1840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.e(LOG_TAG, "dial()", e); 1850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("dial error: " + e); 1860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void switchHoldingAndActive() throws CallStateException { 1900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (DEBUG) Log.d(LOG_TAG, " ~~~~~~ switch fg and bg"); 1910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 1920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville foregroundCall.switchWith(backgroundCall); 1930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (backgroundCall.getState().isAlive()) backgroundCall.hold(); 1940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (foregroundCall.getState().isAlive()) foregroundCall.unhold(); 1950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean canConference() { 1990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return true; 2000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void conference() throws CallStateException { 2030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 2040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if ((foregroundCall.getState() != SipCall.State.ACTIVE) 2050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville || (foregroundCall.getState() != SipCall.State.ACTIVE)) { 2060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("wrong state to merge calls: fg=" 2070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + foregroundCall.getState() + ", bg=" 2080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + backgroundCall.getState()); 2090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville foregroundCall.merge(backgroundCall); 2110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void conference(Call that) throws CallStateException { 2150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 2160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!(that instanceof SipCall)) { 2170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("expect " + SipCall.class 2180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ", cannot merge with " + that.getClass()); 2190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville foregroundCall.merge((SipCall) that); 2210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean canTransfer() { 2250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return false; 2260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void explicitCallTransfer() throws CallStateException { 2290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville //mCT.explicitCallTransfer(); 2300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void clearDisconnected() { 2330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 2340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ringingCall.clearDisconnected(); 2350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville foregroundCall.clearDisconnected(); 2360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville backgroundCall.clearDisconnected(); 2370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville updatePhoneState(); 2390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville notifyPreciseCallStateChanged(); 2400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void sendDtmf(char c) { 2440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!PhoneNumberUtils.is12Key(c)) { 2450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.e(LOG_TAG, 2460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville "sendDtmf called with invalid character '" + c + "'"); 2470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else if (foregroundCall.getState().isAlive()) { 2480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 2490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville foregroundCall.sendDtmf(c); 2500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void startDtmf(char c) { 2550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!PhoneNumberUtils.is12Key(c)) { 2560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.e(LOG_TAG, 2570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville "startDtmf called with invalid character '" + c + "'"); 2580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 2590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sendDtmf(c); 2600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void stopDtmf() { 2640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // no op 2650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void sendBurstDtmf(String dtmfString) { 2680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.e(LOG_TAG, "[SipPhone] sendBurstDtmf() is a CDMA method"); 2690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void getOutgoingCallerIdDisplay(Message onComplete) { 2720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: what to reply? 2730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AsyncResult.forMessage(onComplete, null, null); 2740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onComplete.sendToTarget(); 2750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, 2780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Message onComplete) { 2790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: what's this for SIP? 2800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AsyncResult.forMessage(onComplete, null, null); 2810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onComplete.sendToTarget(); 2820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void getCallWaiting(Message onComplete) { 2850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: what to reply? 2860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AsyncResult.forMessage(onComplete, null, null); 2870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onComplete.sendToTarget(); 2880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void setCallWaiting(boolean enable, Message onComplete) { 2910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: what to reply? 2920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.e(LOG_TAG, "call waiting not supported"); 2930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 2960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void setEchoSuppressionEnabled(boolean enabled) { 2970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // TODO: Remove the enabled argument. We should check the speakerphone 2980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // state with AudioManager instead of keeping a state here so the 2990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // method with a state argument is redundant. Also rename the method 3000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // to something like onSpeaerphoneStateChanged(). Echo suppression may 3010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // not be available on every device. 3020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 3030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville foregroundCall.setAudioGroupMode(); 3040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void setMute(boolean muted) { 3080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 3090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville foregroundCall.setMute(muted); 3100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean getMute() { 3140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return (foregroundCall.getState().isAlive() 3150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ? foregroundCall.getMute() 3160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : backgroundCall.getMute()); 3170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Call getForegroundCall() { 3200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return foregroundCall; 3210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Call getBackgroundCall() { 3240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return backgroundCall; 3250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Call getRingingCall() { 3280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return ringingCall; 3290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public ServiceState getServiceState() { 3320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME: we may need to provide this when data connectivity is lost 3330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // or when server is down 3340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return super.getServiceState(); 3350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private String getUriString(SipProfile p) { 3380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // SipProfile.getUriString() may contain "SIP:" and port 3390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return p.getUserName() + "@" + getSipDomain(p); 3400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private String getSipDomain(SipProfile p) { 3430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String domain = p.getSipDomain(); 3440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // TODO: move this to SipProfile 3450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (domain.endsWith(":5060")) { 3460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return domain.substring(0, domain.length() - 5); 3470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 3480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return domain; 3490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private class SipCall extends SipCallBase { 3530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void reset() { 3540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville connections.clear(); 3550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.IDLE); 3560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void switchWith(SipCall that) { 3590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 3600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipCall tmp = new SipCall(); 3610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville tmp.takeOver(this); 3620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville this.takeOver(that); 3630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville that.takeOver(tmp); 3640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void takeOver(SipCall that) { 3680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville connections = that.connections; 3690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville state = that.state; 3700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (Connection c : connections) { 3710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ((SipConnection) c).changeOwner(this); 3720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 3760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Phone getPhone() { 3770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return SipPhone.this; 3780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 3810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public List<Connection> getConnections() { 3820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 3830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // FIXME should return Collections.unmodifiableList(); 3840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return connections; 3850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Connection dial(String originalNumber) throws SipException { 3890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String calleeSipUri = originalNumber; 3900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!calleeSipUri.contains("@")) { 3910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String replaceStr = Pattern.quote(mProfile.getUserName() + "@"); 3920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville calleeSipUri = mProfile.getUriString().replaceFirst(replaceStr, 3930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville calleeSipUri + "@"); 3940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 3960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipProfile callee = 3970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville new SipProfile.Builder(calleeSipUri).build(); 3980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipConnection c = new SipConnection(this, callee, 3990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville originalNumber); 4000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville c.dial(); 4010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville connections.add(c); 4020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.DIALING); 4030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return c; 4040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (ParseException e) { 4050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new SipException("dial", e); 4060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 4100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void hangup() throws CallStateException { 4110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 4120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (state.isAlive()) { 4130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (DEBUG) Log.d(LOG_TAG, "hang up call: " + getState() 4140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ": " + this + " on phone " + getPhone()); 4150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(State.DISCONNECTING); 4160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville CallStateException excp = null; 4170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (Connection c : connections) { 4180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 4190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville c.hangup(); 4200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (CallStateException e) { 4210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville excp = e; 4220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (excp != null) throw excp; 4250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 4260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (DEBUG) Log.d(LOG_TAG, "hang up dead call: " + getState() 4270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ": " + this + " on phone " + getPhone()); 4280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void initIncomingCall(SipAudioCall sipAudioCall, boolean makeCallWait) { 4330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipProfile callee = sipAudioCall.getPeerProfile(); 4340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipConnection c = new SipConnection(this, callee); 4350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville connections.add(c); 4360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Call.State newState = makeCallWait ? State.WAITING : State.INCOMING; 4380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville c.initIncomingCall(sipAudioCall, newState); 4390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(newState); 4410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville notifyNewRingingConnectionP(c); 4420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void rejectCall() throws CallStateException { 4450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville hangup(); 4460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void acceptCall() throws CallStateException { 4490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (this != ringingCall) { 4500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("acceptCall() in a non-ringing call"); 4510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (connections.size() != 1) { 4530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("acceptCall() in a conf call"); 4540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ((SipConnection) connections.get(0)).acceptCall(); 4560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private boolean isSpeakerOn() { 4590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)) 4600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville .isSpeakerphoneOn(); 4610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void setAudioGroupMode() { 4640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = getAudioGroup(); 4650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (audioGroup == null) return; 4660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int mode = audioGroup.getMode(); 4670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (state == State.HOLDING) { 4680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.setMode(AudioGroup.MODE_ON_HOLD); 4690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else if (getMute()) { 4700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.setMode(AudioGroup.MODE_MUTED); 4710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else if (isSpeakerOn()) { 4720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.setMode(AudioGroup.MODE_ECHO_SUPPRESSION); 4730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 4740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.setMode(AudioGroup.MODE_NORMAL); 4750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (DEBUG) Log.d(LOG_TAG, String.format( 4770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville "audioGroup mode change: %d --> %d", mode, 4780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.getMode())); 4790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void hold() throws CallStateException { 4820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(State.HOLDING); 4830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (Connection c : connections) ((SipConnection) c).hold(); 4840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setAudioGroupMode(); 4850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void unhold() throws CallStateException { 4880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(State.ACTIVE); 4890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = new AudioGroup(); 4900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (Connection c : connections) { 4910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ((SipConnection) c).unhold(audioGroup); 4920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setAudioGroupMode(); 4940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void setMute(boolean muted) { 4970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (Connection c : connections) { 4980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ((SipConnection) c).setMute(muted); 4990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean getMute() { 5030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return connections.isEmpty() 5040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ? false 5050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : ((SipConnection) connections.get(0)).getMute(); 5060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void merge(SipCall that) throws CallStateException { 5090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = getAudioGroup(); 5100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // copy to an array to avoid concurrent modification as connections 5120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // in that.connections will be removed in add(SipConnection). 5130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Connection[] cc = that.connections.toArray( 5140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville new Connection[that.connections.size()]); 5150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (Connection c : cc) { 5160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipConnection conn = (SipConnection) c; 5170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville add(conn); 5180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (conn.getState() == Call.State.HOLDING) { 5190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville conn.unhold(audioGroup); 5200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville that.setState(Call.State.IDLE); 5230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void add(SipConnection conn) { 5260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipCall call = conn.getCall(); 5270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (call == this) return; 5280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (call != null) call.connections.remove(conn); 5290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville connections.add(conn); 5310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville conn.changeOwner(this); 5320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void sendDtmf(char c) { 5350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = getAudioGroup(); 5360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (audioGroup == null) return; 5370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville audioGroup.sendDtmf(convertDtmf(c)); 5380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private int convertDtmf(char c) { 5410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int code = c - '0'; 5420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if ((code < 0) || (code > 9)) { 5430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch (c) { 5440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case '*': return 10; 5450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case '#': return 11; 5460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 'A': return 12; 5470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 'B': return 13; 5480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 'C': return 14; 5490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case 'D': return 15; 5500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville default: 5510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new IllegalArgumentException( 5520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville "invalid DTMF char: " + (int) c); 5530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return code; 5560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 5590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected void setState(State newState) { 5600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (state != newState) { 5610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (DEBUG) Log.v(LOG_TAG, "+***+ call state changed: " + state 5620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + " --> " + newState + ": " + this + ": on phone " 5630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + getPhone() + " " + connections.size()); 5640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (newState == Call.State.ALERTING) { 5660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville state = newState; // need in ALERTING to enable ringback 5670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipPhone.this.startRingbackTone(); 5680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else if (state == Call.State.ALERTING) { 5690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipPhone.this.stopRingbackTone(); 5700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville state = newState; 5720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville updatePhoneState(); 5730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville notifyPreciseCallStateChanged(); 5740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void onConnectionStateChanged(SipConnection conn) { 5780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // this can be called back when a conf call is formed 5790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (state != State.ACTIVE) { 5800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(conn.getState()); 5810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void onConnectionEnded(SipConnection conn) { 5850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // set state to DISCONNECTED only when all conns are disconnected 5860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (state != State.DISCONNECTED) { 5870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean allConnectionsDisconnected = true; 5880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (DEBUG) Log.d(LOG_TAG, "---check connections: " 5890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + connections.size()); 5900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (Connection c : connections) { 5910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (DEBUG) Log.d(LOG_TAG, " state=" + c.getState() + ": " 5920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + c); 5930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (c.getState() != State.DISCONNECTED) { 5940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville allConnectionsDisconnected = false; 5950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 5960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 5980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (allConnectionsDisconnected) setState(State.DISCONNECTED); 5990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville notifyDisconnectP(conn); 6010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private AudioGroup getAudioGroup() { 6040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (connections.isEmpty()) return null; 6050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return ((SipConnection) connections.get(0)).getAudioGroup(); 6060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private class SipConnection extends SipConnectionBase { 6100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipCall mOwner; 6110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipAudioCall mSipAudioCall; 6120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private Call.State mState = Call.State.IDLE; 6130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipProfile mPeer; 6140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private String mOriginalNumber; // may be a PSTN number 6150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private boolean mIncoming = false; 6160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private SipAudioCallAdapter mAdapter = new SipAudioCallAdapter() { 6180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 6190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected void onCallEnded(DisconnectCause cause) { 6200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (getDisconnectCause() != DisconnectCause.LOCAL) { 6210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setDisconnectCause(cause); 6220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 6240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.DISCONNECTED); 6250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipAudioCall sipAudioCall = mSipAudioCall; 6260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall = null; 6270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String sessionState = (sipAudioCall == null) 6280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ? "" 6290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : (sipAudioCall.getState() + ", "); 6300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (DEBUG) Log.d(LOG_TAG, "--- connection ended: " 6310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + mPeer.getUriString() + ": " + sessionState 6320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + "cause: " + getDisconnectCause() + ", on phone " 6330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + getPhone()); 6340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (sipAudioCall != null) { 6350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.setListener(null); 6360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.close(); 6370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOwner.onConnectionEnded(SipConnection.this); 6390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 6430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onCallEstablished(SipAudioCall call) { 6440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onChanged(call); 6450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mState == Call.State.ACTIVE) call.startAudio(); 6460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 6490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onCallHeld(SipAudioCall call) { 6500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onChanged(call); 6510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mState == Call.State.HOLDING) call.startAudio(); 6520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 6550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onChanged(SipAudioCall call) { 6560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 6570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Call.State newState = getCallStateFrom(call); 6580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mState == newState) return; 6590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (newState == Call.State.INCOMING) { 6600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(mOwner.getState()); // INCOMING or WAITING 6610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 6620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mOwner == ringingCall) { 6630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (ringingCall.getState() == Call.State.WAITING) { 6640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 6650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switchHoldingAndActive(); 6660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (CallStateException e) { 6670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // disconnect the call. 6680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onCallEnded(DisconnectCause.LOCAL); 6690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return; 6700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville foregroundCall.switchWith(ringingCall); 6730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(newState); 6750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOwner.onConnectionStateChanged(SipConnection.this); 6770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (DEBUG) Log.v(LOG_TAG, "+***+ connection state changed: " 6780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + mPeer.getUriString() + ": " + mState 6790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + " on phone " + getPhone()); 6800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 6840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected void onError(DisconnectCause cause) { 6850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (DEBUG) Log.d(LOG_TAG, "SIP error: " + cause); 6860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onCallEnded(cause); 6870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville }; 6890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public SipConnection(SipCall owner, SipProfile callee, 6910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String originalNumber) { 6920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville super(originalNumber); 6930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOwner = owner; 6940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mPeer = callee; 6950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOriginalNumber = originalNumber; 6960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public SipConnection(SipCall owner, SipProfile callee) { 6990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville this(owner, callee, getUriString(callee)); 7000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 7030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public String getCnapName() { 7040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String displayName = mPeer.getDisplayName(); 7050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return TextUtils.isEmpty(displayName) ? null 7060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : displayName; 7070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 7100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public int getNumberPresentation() { 7110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return PhoneConstants.PRESENTATION_ALLOWED; 7120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void initIncomingCall(SipAudioCall sipAudioCall, Call.State newState) { 7150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(newState); 7160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall = sipAudioCall; 7170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.setListener(mAdapter); // call back to set state 7180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mIncoming = true; 7190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void acceptCall() throws CallStateException { 7220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 7230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.answerCall(TIMEOUT_ANSWER_CALL); 7240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 7250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("acceptCall(): " + e); 7260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void changeOwner(SipCall owner) { 7300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mOwner = owner; 7310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup getAudioGroup() { 7340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mSipAudioCall == null) return null; 7350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mSipAudioCall.getAudioGroup(); 7360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void dial() throws SipException { 7390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.DIALING); 7400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall = mSipManager.makeAudioCall(mProfile, mPeer, null, 7410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville TIMEOUT_MAKE_CALL); 7420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.setListener(mAdapter); 7430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void hold() throws CallStateException { 7460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.HOLDING); 7470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 7480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.holdCall(TIMEOUT_HOLD_CALL); 7490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 7500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("hold(): " + e); 7510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void unhold(AudioGroup audioGroup) throws CallStateException { 7550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.setAudioGroup(audioGroup); 7560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville setState(Call.State.ACTIVE); 7570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 7580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.continueCall(TIMEOUT_HOLD_CALL); 7590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 7600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("unhold(): " + e); 7610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void setMute(boolean muted) { 7650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if ((mSipAudioCall != null) && (muted != mSipAudioCall.isMuted())) { 7660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.toggleMute(); 7670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean getMute() { 7710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return (mSipAudioCall == null) ? false 7720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : mSipAudioCall.isMuted(); 7730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 7760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected void setState(Call.State state) { 7770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (state == mState) return; 7780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville super.setState(state); 7790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mState = state; 7800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 7830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public Call.State getState() { 7840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mState; 7850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 7880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean isIncoming() { 7890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mIncoming; 7900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 7920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 7930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public String getAddress() { 7940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Phone app uses this to query caller ID. Return the original dial 7950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // number (which may be a PSTN number) instead of the peer's SIP 7960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // URI. 7970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mOriginalNumber; 7980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 7990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 8010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public SipCall getCall() { 8020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mOwner; 8030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 8060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected Phone getPhone() { 8070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return mOwner.getPhone(); 8080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 8110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void hangup() throws CallStateException { 8120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 8130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (DEBUG) Log.d(LOG_TAG, "hangup conn: " + mPeer.getUriString() 8140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ": " + mState + ": on phone " 8150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + getPhone().getPhoneName()); 8160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!mState.isAlive()) return; 8170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 8180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipAudioCall sipAudioCall = mSipAudioCall; 8190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (sipAudioCall != null) { 8200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.setListener(null); 8210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sipAudioCall.endCall(); 8220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (SipException e) { 8240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException("hangup(): " + e); 8250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } finally { 8260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mAdapter.onCallEnded(((mState == Call.State.INCOMING) 8270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville || (mState == Call.State.WAITING)) 8280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ? DisconnectCause.INCOMING_REJECTED 8290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : DisconnectCause.LOCAL); 8300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 8350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void separate() throws CallStateException { 8360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (SipPhone.class) { 8370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SipCall call = (getPhone() == SipPhone.this) 8380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ? (SipCall) SipPhone.this.getBackgroundCall() 8390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : (SipCall) SipPhone.this.getForegroundCall(); 8400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (call.getState() != Call.State.IDLE) { 8410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throw new CallStateException( 8420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville "cannot put conn back to a call in non-idle state: " 8430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + call.getState()); 8440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (DEBUG) Log.d(LOG_TAG, "separate conn: " 8460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + mPeer.getUriString() + " from " + mOwner + " back to " 8470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + call); 8480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // separate the AudioGroup and connection from the original call 8500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Phone originalPhone = getPhone(); 8510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville AudioGroup audioGroup = call.getAudioGroup(); // may be null 8520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville call.add(this); 8530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.setAudioGroup(audioGroup); 8540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // put the original call to bg; and the separated call becomes 8560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // fg if it was in bg 8570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville originalPhone.switchHoldingAndActive(); 8580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // start audio and notify the phone app of the state change 8600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville call = (SipCall) SipPhone.this.getForegroundCall(); 8610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSipAudioCall.startAudio(); 8620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville call.onConnectionStateChanged(this); 8630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static Call.State getCallStateFrom(SipAudioCall sipAudioCall) { 8690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (sipAudioCall.isOnHold()) return Call.State.HOLDING; 8700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int sessionState = sipAudioCall.getState(); 8710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch (sessionState) { 8720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipSession.State.READY_TO_CALL: return Call.State.IDLE; 8730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipSession.State.INCOMING_CALL: 8740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipSession.State.INCOMING_CALL_ANSWERING: return Call.State.INCOMING; 8750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipSession.State.OUTGOING_CALL: return Call.State.DIALING; 8760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipSession.State.OUTGOING_CALL_RING_BACK: return Call.State.ALERTING; 8770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipSession.State.OUTGOING_CALL_CANCELING: return Call.State.DISCONNECTING; 8780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipSession.State.IN_CALL: return Call.State.ACTIVE; 8790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville default: 8800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.w(LOG_TAG, "illegal connection state: " + sessionState); 8810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return Call.State.DISCONNECTED; 8820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private abstract class SipAudioCallAdapter extends SipAudioCall.Listener { 8860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected abstract void onCallEnded(Connection.DisconnectCause cause); 8870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected abstract void onError(Connection.DisconnectCause cause); 8880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 8900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onCallEnded(SipAudioCall call) { 8910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onCallEnded(call.isInCall() 8920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ? Connection.DisconnectCause.NORMAL 8930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville : Connection.DisconnectCause.INCOMING_MISSED); 8940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 8950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 8960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 8970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onCallBusy(SipAudioCall call) { 8980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onCallEnded(Connection.DisconnectCause.BUSY); 8990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 9010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 9020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onError(SipAudioCall call, int errorCode, 9030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String errorMessage) { 9040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch (errorCode) { 9050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.SERVER_UNREACHABLE: 9060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onError(Connection.DisconnectCause.SERVER_UNREACHABLE); 9070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 9080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.PEER_NOT_REACHABLE: 9090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onError(Connection.DisconnectCause.NUMBER_UNREACHABLE); 9100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 9110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.INVALID_REMOTE_URI: 9120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onError(Connection.DisconnectCause.INVALID_NUMBER); 9130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 9140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.TIME_OUT: 9150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.TRANSACTION_TERMINTED: 9160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onError(Connection.DisconnectCause.TIMED_OUT); 9170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 9180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.DATA_CONNECTION_LOST: 9190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onError(Connection.DisconnectCause.LOST_SIGNAL); 9200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 9210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.INVALID_CREDENTIALS: 9220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onError(Connection.DisconnectCause.INVALID_CREDENTIALS); 9230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 9240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.CROSS_DOMAIN_AUTHENTICATION: 9250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onError(Connection.DisconnectCause.OUT_OF_NETWORK); 9260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 9270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.SERVER_ERROR: 9280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onError(Connection.DisconnectCause.SERVER_ERROR); 9290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 9300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.SOCKET_ERROR: 9310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SipErrorCode.CLIENT_ERROR: 9320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville default: 9330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.w(LOG_TAG, "error: " + SipErrorCode.toString(errorCode) 9340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ": " + errorMessage); 9350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville onError(Connection.DisconnectCause.ERROR_UNSPECIFIED); 9360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 9390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville} 940