SipPhone.java revision b7b7a62112b79571adf74372c5f5366fd62d0031
1c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet/* 2c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet * Copyright (C) 2010 The Android Open Source Project 3c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet * 4c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet * Licensed under the Apache License, Version 2.0 (the "License"); 5c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet * you may not use this file except in compliance with the License. 6c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet * You may obtain a copy of the License at 7c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet * 8c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet * http://www.apache.org/licenses/LICENSE-2.0 9c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet * 10c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet * Unless required by applicable law or agreed to in writing, software 11c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet * distributed under the License is distributed on an "AS IS" BASIS, 12c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet * See the License for the specific language governing permissions and 14c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet * limitations under the License. 15c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet */ 16c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 1732d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sungpackage com.android.internal.telephony.sip; 18c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 19c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouilletimport android.content.Context; 20c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouilletimport android.media.AudioManager; 21c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouilletimport android.net.rtp.AudioGroup; 22c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouilletimport android.net.sip.SipAudioCall; 23c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouilletimport android.net.sip.SipErrorCode; 24c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouilletimport android.net.sip.SipException; 25c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouilletimport android.net.sip.SipManager; 26c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouilletimport android.net.sip.SipProfile; 27c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouilletimport android.net.sip.SipSession; 28c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouilletimport android.os.AsyncResult; 29c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouilletimport android.os.Message; 30c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouilletimport android.telephony.DisconnectCause; 31c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouilletimport android.telephony.PhoneNumberUtils; 322217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouilletimport android.telephony.ServiceState; 332217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouilletimport android.text.TextUtils; 3432d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sungimport android.telephony.Rlog; 3532d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung 3632d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sungimport com.android.internal.telephony.Call; 3732d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sungimport com.android.internal.telephony.CallStateException; 3832d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sungimport com.android.internal.telephony.Connection; 3932d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sungimport com.android.internal.telephony.Phone; 4032d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sungimport com.android.internal.telephony.PhoneConstants; 4132d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sungimport com.android.internal.telephony.PhoneNotifier; 4232d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung 4332d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sungimport java.text.ParseException; 4432d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sungimport java.util.List; 4532d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sungimport java.util.regex.Pattern; 4632d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung 4732d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung/** 4832d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung * {@hide} 4932d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung */ 5032d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sungpublic class SipPhone extends SipPhoneBase { 5132d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung private static final String LOG_TAG = "SipPhone"; 5232d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung private static final boolean DBG = true; 536e6b65617d3a779d939e7d6dd26663a898205ee3I-Jui (Ray) Sung private static final boolean VDBG = false; // STOPSHIP if true 5432d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung private static final int TIMEOUT_MAKE_CALL = 15; // in seconds 5532d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung private static final int TIMEOUT_ANSWER_CALL = 8; // in seconds 5632d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung private static final int TIMEOUT_HOLD_CALL = 15; // in seconds 5732d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung 582217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet // A call that is ringing or (call) waiting 5932d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung private SipCall mRingingCall = new SipCall(); 6032d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung private SipCall mForegroundCall = new SipCall(); 61918944e41306198807728ae3f05a1799773895c5Jean-Luc Brouillet private SipCall mBackgroundCall = new SipCall(); 62c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 63c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet private SipManager mSipManager; 6432d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung private SipProfile mProfile; 65918944e41306198807728ae3f05a1799773895c5Jean-Luc Brouillet 6632d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung SipPhone (Context context, PhoneNotifier notifier, SipProfile profile) { 6732d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung super("SIP:" + profile.getUriString(), context, notifier); 68c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 69c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (DBG) log("new SipPhone: " + profile.getUriString()); 70c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mRingingCall = new SipCall(); 71c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mForegroundCall = new SipCall(); 72c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mBackgroundCall = new SipCall(); 73c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mProfile = profile; 74c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mSipManager = SipManager.newInstance(context); 75c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 76c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 77c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet @Override 78c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet public boolean equals(Object o) { 79c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (o == this) return true; 80c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (!(o instanceof SipPhone)) return false; 81c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet SipPhone that = (SipPhone) o; 82c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet return mProfile.getUriString().equals(that.mProfile.getUriString()); 83c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 84c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 85c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet public String getSipUri() { 86c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet return mProfile.getUriString(); 87c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 88c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 89c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet public boolean equals(SipPhone phone) { 90c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet return getSipUri().equals(phone.getSipUri()); 91c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 92c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 93c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet public boolean canTake(Object incomingCall) { 94c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // FIXME: Is synchronizing on the class necessary, should we use a mLockObj? 95c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // Also there are many things not synchronized, of course 96c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // this may be true of CdmaPhone and GsmPhone too!!! 97c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet synchronized (SipPhone.class) { 98c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (!(incomingCall instanceof SipAudioCall)) { 99c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (DBG) log("canTake: ret=false, not a SipAudioCall"); 100c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet return false; 101c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 102c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (mRingingCall.getState().isAlive()) { 103c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (DBG) log("canTake: ret=false, ringingCall not alive"); 104c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet return false; 105c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 106c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 107c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // FIXME: is it true that we cannot take any incoming call if 108c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // both foreground and background are active 109c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (mForegroundCall.getState().isAlive() 110c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet && mBackgroundCall.getState().isAlive()) { 111c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (DBG) { 112c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet log("canTake: ret=false," + 113c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet " foreground and background both alive"); 114c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 115c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet return false; 116c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 117c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 118c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet try { 119c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet SipAudioCall sipAudioCall = (SipAudioCall) incomingCall; 120c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (DBG) log("canTake: taking call from: " 121c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet + sipAudioCall.getPeerProfile().getUriString()); 122c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet String localUri = sipAudioCall.getLocalProfile().getUriString(); 123c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (localUri.equals(mProfile.getUriString())) { 124c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet boolean makeCallWait = mForegroundCall.getState().isAlive(); 125c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mRingingCall.initIncomingCall(sipAudioCall, makeCallWait); 126c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (sipAudioCall.getState() 127c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet != SipSession.State.INCOMING_CALL) { 128c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // Peer cancelled the call! 129c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (DBG) log(" canTake: call cancelled !!"); 130c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mRingingCall.reset(); 131c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 132c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet return true; 133c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 134c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } catch (Exception e) { 135c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // Peer may cancel the call at any time during the time we hook 136c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // up ringingCall with sipAudioCall. Clean up ringingCall when 137c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // that happens. 138c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (DBG) log(" canTake: exception e=" + e); 139c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mRingingCall.reset(); 140c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 141c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (DBG) log("canTake: NOT taking !!"); 142c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet return false; 143c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 144c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 145c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 146c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet @Override 147c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet public void acceptCall() throws CallStateException { 148c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet synchronized (SipPhone.class) { 149c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if ((mRingingCall.getState() == Call.State.INCOMING) || 150c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet (mRingingCall.getState() == Call.State.WAITING)) { 151c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (DBG) log("acceptCall: accepting"); 152c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // Always unmute when answering a new call 153c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mRingingCall.setMute(false); 154c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mRingingCall.acceptCall(); 155c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } else { 156c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (DBG) { 157c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet log("acceptCall:" + 158c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet " throw CallStateException(\"phone not ringing\")"); 159c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 160c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet throw new CallStateException("phone not ringing"); 161c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 162c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 163c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 164c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 165c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet @Override 166c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet public void rejectCall() throws CallStateException { 167c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet synchronized (SipPhone.class) { 168c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (mRingingCall.getState().isRinging()) { 169c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (DBG) log("rejectCall: rejecting"); 170c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mRingingCall.rejectCall(); 171c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } else { 172c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (DBG) { 173c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet log("rejectCall:" + 174c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet " throw CallStateException(\"phone not ringing\")"); 175c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 176c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet throw new CallStateException("phone not ringing"); 177c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 178c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 179c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 180c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 181c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet @Override 182c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet public Connection dial(String dialString) throws CallStateException { 183c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet synchronized (SipPhone.class) { 184c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet return dialInternal(dialString); 185c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 186c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 187c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 188c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet private Connection dialInternal(String dialString) 189c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet throws CallStateException { 190c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (DBG) log("dialInternal: dialString=" + (VDBG ? dialString : "xxxxxx")); 191c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet clearDisconnected(); 192c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 193c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (!canDial()) { 194c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet throw new CallStateException("dialInternal: cannot dial in current state"); 195c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 196c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (mForegroundCall.getState() == SipCall.State.ACTIVE) { 197c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet switchHoldingAndActive(); 198c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 199c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (mForegroundCall.getState() != SipCall.State.IDLE) { 200c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet //we should have failed in !canDial() above before we get here 201c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet throw new CallStateException("cannot dial in current state"); 202c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 203c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 204c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mForegroundCall.setMute(false); 205c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet try { 206c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet Connection c = mForegroundCall.dial(dialString); 207c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet return c; 208c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } catch (SipException e) { 209c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet loge("dialInternal: ", e); 210c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet throw new CallStateException("dial error: " + e); 211c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 212c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 213c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 214c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet @Override 215c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet public void switchHoldingAndActive() throws CallStateException { 216c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (DBG) log("dialInternal: switch fg and bg"); 217c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet synchronized (SipPhone.class) { 218c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mForegroundCall.switchWith(mBackgroundCall); 219c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (mBackgroundCall.getState().isAlive()) mBackgroundCall.hold(); 220c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (mForegroundCall.getState().isAlive()) mForegroundCall.unhold(); 221c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 222c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 223c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 224c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet @Override 225c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet public boolean canConference() { 226c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (DBG) log("canConference: ret=true"); 227c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet return true; 228c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 2294a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet 230c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet @Override 2316386ceb3bf25e442513224aaa45691dfe49562d9Jean-Luc Brouillet public void conference() throws CallStateException { 232c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet synchronized (SipPhone.class) { 2334a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet if ((mForegroundCall.getState() != SipCall.State.ACTIVE) 234c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet || (mForegroundCall.getState() != SipCall.State.ACTIVE)) { 2354a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet throw new CallStateException("wrong state to merge calls: fg=" 236c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet + mForegroundCall.getState() + ", bg=" 237c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet + mBackgroundCall.getState()); 238c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 239c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (DBG) log("conference: merge fg & bg"); 240c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mForegroundCall.merge(mBackgroundCall); 241c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 242c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 2434a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet 2444a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet public void conference(Call that) throws CallStateException { 2454a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet synchronized (SipPhone.class) { 2464a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet if (!(that instanceof SipCall)) { 2474a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet throw new CallStateException("expect " + SipCall.class 2484a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet + ", cannot merge with " + that.getClass()); 2494a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } 2504a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet mForegroundCall.merge((SipCall) that); 2514a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } 252c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 253c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 2544a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet @Override 2554a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet public boolean canTransfer() { 2564a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet return false; 257c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 258c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 2594a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet @Override 2604a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet public void explicitCallTransfer() { 2614a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet //mCT.explicitCallTransfer(); 2622217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet } 2632217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet 2644a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet @Override 2654a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet public void clearDisconnected() { 2664a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet synchronized (SipPhone.class) { 2674a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet mRingingCall.clearDisconnected(); 2684a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet mForegroundCall.clearDisconnected(); 2694a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet mBackgroundCall.clearDisconnected(); 2704a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet 2714a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet updatePhoneState(); 2724a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet notifyPreciseCallStateChanged(); 273c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 274c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 2754a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet 2764a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet @Override 2774a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet public void sendDtmf(char c) { 2784a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet if (!PhoneNumberUtils.is12Key(c)) { 2794a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet loge("sendDtmf called with invalid character '" + c + "'"); 2804a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } else if (mForegroundCall.getState().isAlive()) { 2814a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet synchronized (SipPhone.class) { 2824a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet mForegroundCall.sendDtmf(c); 2834a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } 2844a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } 2854a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } 2864a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet 287c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet @Override 288c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet public void startDtmf(char c) { 289c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (!PhoneNumberUtils.is12Key(c)) { 290c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet loge("startDtmf called with invalid character '" + c + "'"); 2914a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } else { 2924a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet sendDtmf(c); 2934a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } 2944a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } 2954a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet 296c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet @Override 2974a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet public void stopDtmf() { 298c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // no op 2994a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } 3004a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet 3014a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet public void sendBurstDtmf(String dtmfString) { 302c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet loge("sendBurstDtmf() is a CDMA method"); 3034a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } 304c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 3054a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet @Override 3064a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet public void getOutgoingCallerIdDisplay(Message onComplete) { 3074a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet // FIXME: what to reply? 308c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet AsyncResult.forMessage(onComplete, null, null); 3094a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet onComplete.sendToTarget(); 310c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 311c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 312ca51c78b9e3097ee31dd24cdc5982f550ee563d1Stephen Hines @Override 313ca51c78b9e3097ee31dd24cdc5982f550ee563d1Stephen Hines public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, 3144a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet Message onComplete) { 315c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // FIXME: what's this for SIP? 3164a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet AsyncResult.forMessage(onComplete, null, null); 317c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet onComplete.sendToTarget(); 3184a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } 319c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 320c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet @Override 321c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet public void getCallWaiting(Message onComplete) { 322c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // FIXME: what to reply? 323c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet AsyncResult.forMessage(onComplete, null, null); 324c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet onComplete.sendToTarget(); 325c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 326c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 327c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet @Override 328c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet public void setCallWaiting(boolean enable, Message onComplete) { 329ffc1710c382168e74386ef87d0101de65aca91b0Jean-Luc Brouillet // FIXME: what to reply? 330c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet loge("call waiting not supported"); 331c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 332c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 333c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet @Override 3344a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet public void setEchoSuppressionEnabled(boolean enabled) { 335c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // TODO: Remove the enabled argument. We should check the speakerphone 3364a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet // state with AudioManager instead of keeping a state here so the 337c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // method with a state argument is redundant. Also rename the method 338c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // to something like onSpeaerphoneStateChanged(). Echo suppression may 3394a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet // not be available on every device. 3404a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet synchronized (SipPhone.class) { 341ca51c78b9e3097ee31dd24cdc5982f550ee563d1Stephen Hines mForegroundCall.setAudioGroupMode(); 342ca51c78b9e3097ee31dd24cdc5982f550ee563d1Stephen Hines } 343ca51c78b9e3097ee31dd24cdc5982f550ee563d1Stephen Hines } 344ca51c78b9e3097ee31dd24cdc5982f550ee563d1Stephen Hines 345ca51c78b9e3097ee31dd24cdc5982f550ee563d1Stephen Hines @Override 3464a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet public void setMute(boolean muted) { 347ca51c78b9e3097ee31dd24cdc5982f550ee563d1Stephen Hines synchronized (SipPhone.class) { 348c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mForegroundCall.setMute(muted); 349c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 350c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 35136e2be56cd398bf4a318114bbc9fa3f4573c158fJean-Luc Brouillet 35236e2be56cd398bf4a318114bbc9fa3f4573c158fJean-Luc Brouillet @Override 35336e2be56cd398bf4a318114bbc9fa3f4573c158fJean-Luc Brouillet public boolean getMute() { 35436e2be56cd398bf4a318114bbc9fa3f4573c158fJean-Luc Brouillet return (mForegroundCall.getState().isAlive() 35536e2be56cd398bf4a318114bbc9fa3f4573c158fJean-Luc Brouillet ? mForegroundCall.getMute() 356ca51c78b9e3097ee31dd24cdc5982f550ee563d1Stephen Hines : mBackgroundCall.getMute()); 35736e2be56cd398bf4a318114bbc9fa3f4573c158fJean-Luc Brouillet } 3584a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet 359ca51c78b9e3097ee31dd24cdc5982f550ee563d1Stephen Hines @Override 360ca51c78b9e3097ee31dd24cdc5982f550ee563d1Stephen Hines public Call getForegroundCall() { 361ca51c78b9e3097ee31dd24cdc5982f550ee563d1Stephen Hines return mForegroundCall; 362ca51c78b9e3097ee31dd24cdc5982f550ee563d1Stephen Hines } 363ca51c78b9e3097ee31dd24cdc5982f550ee563d1Stephen Hines 364ca51c78b9e3097ee31dd24cdc5982f550ee563d1Stephen Hines @Override 365ca51c78b9e3097ee31dd24cdc5982f550ee563d1Stephen Hines public Call getBackgroundCall() { 3664a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet return mBackgroundCall; 367c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 36836e2be56cd398bf4a318114bbc9fa3f4573c158fJean-Luc Brouillet 369c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet @Override 3704a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet public Call getRingingCall() { 371ca51c78b9e3097ee31dd24cdc5982f550ee563d1Stephen Hines return mRingingCall; 3724a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } 373c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 3744a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet @Override 37536e2be56cd398bf4a318114bbc9fa3f4573c158fJean-Luc Brouillet public ServiceState getServiceState() { 37636e2be56cd398bf4a318114bbc9fa3f4573c158fJean-Luc Brouillet // FIXME: we may need to provide this when data connectivity is lost 377c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // or when server is down 3784a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet return super.getServiceState(); 3794a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } 3804a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet 381c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet private String getUriString(SipProfile p) { 3824a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet // SipProfile.getUriString() may contain "SIP:" and port 383c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet return p.getUserName() + "@" + getSipDomain(p); 3844a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } 385c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 386c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet private String getSipDomain(SipProfile p) { 387c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet String domain = p.getSipDomain(); 3884a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet // TODO: move this to SipProfile 389ca51c78b9e3097ee31dd24cdc5982f550ee563d1Stephen Hines if (domain.endsWith(":5060")) { 3904a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet return domain.substring(0, domain.length() - 5); 3914a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } else { 3924a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet return domain; 39336e2be56cd398bf4a318114bbc9fa3f4573c158fJean-Luc Brouillet } 39436e2be56cd398bf4a318114bbc9fa3f4573c158fJean-Luc Brouillet } 395c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 3964a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet private static Call.State getCallStateFrom(SipAudioCall sipAudioCall) { 397c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (sipAudioCall.isOnHold()) return Call.State.HOLDING; 3984a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet int sessionState = sipAudioCall.getState(); 399c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet switch (sessionState) { 4004a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet case SipSession.State.READY_TO_CALL: return Call.State.IDLE; 401c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet case SipSession.State.INCOMING_CALL: 4024a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet case SipSession.State.INCOMING_CALL_ANSWERING: return Call.State.INCOMING; 403c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet case SipSession.State.OUTGOING_CALL: return Call.State.DIALING; 404c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet case SipSession.State.OUTGOING_CALL_RING_BACK: return Call.State.ALERTING; 405c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet case SipSession.State.OUTGOING_CALL_CANCELING: return Call.State.DISCONNECTING; 406c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet case SipSession.State.IN_CALL: return Call.State.ACTIVE; 407c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet default: 4084a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet slog("illegal connection state: " + sessionState); 4094a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet return Call.State.DISCONNECTED; 410c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 411ca51c78b9e3097ee31dd24cdc5982f550ee563d1Stephen Hines } 4124a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet 4134a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet private void log(String s) { 414c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet Rlog.d(LOG_TAG, s); 415c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 416c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 417c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet private static void slog(String s) { 418c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet Rlog.d(LOG_TAG, s); 419c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 420c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 421c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet private void loge(String s) { 422c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet Rlog.e(LOG_TAG, s); 423c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 424c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 4257c07854a959eb70ff9623202b2ca064407a1cc68Jean-Luc Brouillet private void loge(String s, Exception e) { 4264a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet Rlog.e(LOG_TAG, s, e); 4274a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } 428c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 429c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet private class SipCall extends SipCallBase { 430c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet private static final String SC_TAG = "SipCall"; 43132d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung private static final boolean SC_DBG = true; 432c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet private static final boolean SC_VDBG = false; // STOPSHIP if true 43332d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung 434c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet void reset() { 435c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (SC_DBG) log("reset"); 436c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mConnections.clear(); 437c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet setState(Call.State.IDLE); 43832d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung } 43932d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung 44032d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung void switchWith(SipCall that) { 44132d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung if (SC_DBG) log("switchWith"); 442c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet synchronized (SipPhone.class) { 443c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet SipCall tmp = new SipCall(); 444c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet tmp.takeOver(this); 445c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet this.takeOver(that); 446c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet that.takeOver(tmp); 447c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 448c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 44932d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung 450c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet private void takeOver(SipCall that) { 451c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (SC_DBG) log("takeOver"); 452c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mConnections = that.mConnections; 453c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mState = that.mState; 45432d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung for (Connection c : mConnections) { 455c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet ((SipConnection) c).changeOwner(this); 45632d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung } 457c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 458c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 45932d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung @Override 460c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet public Phone getPhone() { 461c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet return SipPhone.this; 4624a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } 4634a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet 4644a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet @Override 4654a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet public List<Connection> getConnections() { 466c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (SC_VDBG) log("getConnections"); 46732d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung synchronized (SipPhone.class) { 468c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // FIXME should return Collections.unmodifiableList(); 469c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet return mConnections; 470c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 471c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 4724a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet 4734a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet Connection dial(String originalNumber) throws SipException { 4744a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet if (SC_DBG) log("dial: num=" + (SC_VDBG ? originalNumber : "xxx")); 4754a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet // TODO: Should this be synchronized? 4764a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet String calleeSipUri = originalNumber; 4774a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet if (!calleeSipUri.contains("@")) { 4784a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet String replaceStr = Pattern.quote(mProfile.getUserName() + "@"); 4794a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet calleeSipUri = mProfile.getUriString().replaceFirst(replaceStr, 4804a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet calleeSipUri + "@"); 4814a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } 4824a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet try { 4834a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet SipProfile callee = 4844a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet new SipProfile.Builder(calleeSipUri).build(); 4854a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet SipConnection c = new SipConnection(this, callee, 486c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet originalNumber); 487c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet c.dial(); 488c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mConnections.add(c); 489c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet setState(Call.State.DIALING); 490c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet return c; 491c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } catch (ParseException e) { 492918944e41306198807728ae3f05a1799773895c5Jean-Luc Brouillet throw new SipException("dial", e); 493c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 494c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 495c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 496c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet @Override 497c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet public void hangup() throws CallStateException { 498c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet synchronized (SipPhone.class) { 499c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (mState.isAlive()) { 500c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (SC_DBG) log("hangup: call " + getState() 5014a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet + ": " + this + " on phone " + getPhone()); 5024a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet setState(State.DISCONNECTING); 5034a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet CallStateException excp = null; 5044a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet for (Connection c : mConnections) { 5054a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet try { 5064a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet c.hangup(); 5074a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } catch (CallStateException e) { 508c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet excp = e; 509c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 510c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 511c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (excp != null) throw excp; 512c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } else { 513c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (SC_DBG) log("hangup: dead call " + getState() 5144a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet + ": " + this + " on phone " + getPhone()); 515c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 516c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 517c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 518c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 519c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet void initIncomingCall(SipAudioCall sipAudioCall, boolean makeCallWait) { 520c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet SipProfile callee = sipAudioCall.getPeerProfile(); 521c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet SipConnection c = new SipConnection(this, callee); 522c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mConnections.add(c); 523c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 524c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet Call.State newState = makeCallWait ? State.WAITING : State.INCOMING; 525c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet c.initIncomingCall(sipAudioCall, newState); 526c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 527c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet setState(newState); 528c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet notifyNewRingingConnectionP(c); 529c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 530c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 531918944e41306198807728ae3f05a1799773895c5Jean-Luc Brouillet void rejectCall() throws CallStateException { 532c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (SC_DBG) log("rejectCall:"); 533c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet hangup(); 534c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 535c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 536c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet void acceptCall() throws CallStateException { 537c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (SC_DBG) log("acceptCall: accepting"); 538c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (this != mRingingCall) { 5394a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet throw new CallStateException("acceptCall() in a non-ringing call"); 5404a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } 541c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (mConnections.size() != 1) { 542c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet throw new CallStateException("acceptCall() in a conf call"); 5434a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet } 544c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet ((SipConnection) mConnections.get(0)).acceptCall(); 545c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 546c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 547c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet private boolean isSpeakerOn() { 5484a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet Boolean ret = ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)) 549c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet .isSpeakerphoneOn(); 550c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (SC_VDBG) log("isSpeakerOn: ret=" + ret); 551c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet return ret; 552c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 553c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 554c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet void setAudioGroupMode() { 5556386ceb3bf25e442513224aaa45691dfe49562d9Jean-Luc Brouillet AudioGroup audioGroup = getAudioGroup(); 5566386ceb3bf25e442513224aaa45691dfe49562d9Jean-Luc Brouillet if (audioGroup == null) { 5576386ceb3bf25e442513224aaa45691dfe49562d9Jean-Luc Brouillet if (SC_DBG) log("setAudioGroupMode: audioGroup == null ignore"); 558c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet return; 559c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 560918944e41306198807728ae3f05a1799773895c5Jean-Luc Brouillet int mode = audioGroup.getMode(); 561c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (mState == State.HOLDING) { 562c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet audioGroup.setMode(AudioGroup.MODE_ON_HOLD); 563c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } else if (getMute()) { 564c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet audioGroup.setMode(AudioGroup.MODE_MUTED); 565c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } else if (isSpeakerOn()) { 566c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet audioGroup.setMode(AudioGroup.MODE_ECHO_SUPPRESSION); 567c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } else { 568c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet audioGroup.setMode(AudioGroup.MODE_NORMAL); 569c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 570c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (SC_DBG) log(String.format( 571c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet "setAudioGroupMode change: %d --> %d", mode, 5724a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet audioGroup.getMode())); 573c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 574c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 5754a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet void hold() throws CallStateException { 576c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (SC_DBG) log("hold:"); 577ca51c78b9e3097ee31dd24cdc5982f550ee563d1Stephen Hines setState(State.HOLDING); 5784a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet for (Connection c : mConnections) ((SipConnection) c).hold(); 579c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet setAudioGroupMode(); 580c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 581c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 582c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet void unhold() throws CallStateException { 583c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (SC_DBG) log("unhold:"); 584c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet setState(State.ACTIVE); 585c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet AudioGroup audioGroup = new AudioGroup(); 586c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet for (Connection c : mConnections) { 587c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet ((SipConnection) c).unhold(audioGroup); 588c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 589c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet setAudioGroupMode(); 590c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 591c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 592c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet void setMute(boolean muted) { 593c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (SC_DBG) log("setMute: muted=" + muted); 594c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet for (Connection c : mConnections) { 595c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet ((SipConnection) c).setMute(muted); 596c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 597c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 598c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 599c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet boolean getMute() { 600c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet boolean ret = mConnections.isEmpty() 601c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet ? false 602c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet : ((SipConnection) mConnections.get(0)).getMute(); 603c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (SC_DBG) log("getMute: ret=" + ret); 604c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet return ret; 605c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 6064a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet 607c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet void merge(SipCall that) throws CallStateException { 608c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (SC_DBG) log("merge:"); 609c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet AudioGroup audioGroup = getAudioGroup(); 610c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 611c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // copy to an array to avoid concurrent modification as connections 612c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // in that.connections will be removed in add(SipConnection). 613c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet Connection[] cc = that.mConnections.toArray( 614c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet new Connection[that.mConnections.size()]); 615c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet for (Connection c : cc) { 616c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet SipConnection conn = (SipConnection) c; 61732d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung add(conn); 61832d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung if (conn.getState() == Call.State.HOLDING) { 6192217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet conn.unhold(audioGroup); 6202217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet } 6212217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet } 6222217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet that.setState(Call.State.IDLE); 6232217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet } 624c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 6252217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet private void add(SipConnection conn) { 62632d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung if (SC_DBG) log("add:"); 6272217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet SipCall call = conn.getCall(); 628c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (call == this) return; 629c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (call != null) call.mConnections.remove(conn); 630c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 631c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mConnections.add(conn); 6322217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet conn.changeOwner(this); 63332d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung } 634c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 635c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet void sendDtmf(char c) { 636c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (SC_DBG) log("sendDtmf: c=" + c); 637c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet AudioGroup audioGroup = getAudioGroup(); 638c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (audioGroup == null) { 639c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (SC_DBG) log("sendDtmf: audioGroup == null, ignore c=" + c); 640c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet return; 641c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 6427c07854a959eb70ff9623202b2ca064407a1cc68Jean-Luc Brouillet audioGroup.sendDtmf(convertDtmf(c)); 6437c07854a959eb70ff9623202b2ca064407a1cc68Jean-Luc Brouillet } 6447c07854a959eb70ff9623202b2ca064407a1cc68Jean-Luc Brouillet 6454a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet private int convertDtmf(char c) { 6464a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet int code = c - '0'; 6474a73004df5231d188c41267fee17c566ae7c3631Jean-Luc Brouillet if ((code < 0) || (code > 9)) { 648c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet switch (c) { 649c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet case '*': return 10; 650c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet case '#': return 11; 651c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet case 'A': return 12; 652c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet case 'B': return 13; 653c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet case 'C': return 14; 654c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet case 'D': return 15; 655c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet default: 656c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet throw new IllegalArgumentException( 657c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet "invalid DTMF char: " + (int) c); 658c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 659c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 660c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet return code; 661c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 662c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 663c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet @Override 664c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet protected void setState(State newState) { 665c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (mState != newState) { 666c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (SC_DBG) log("setState: cur state" + mState 667c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet + " --> " + newState + ": " + this + ": on phone " 668c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet + getPhone() + " " + mConnections.size()); 669c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 670c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (newState == Call.State.ALERTING) { 671c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mState = newState; // need in ALERTING to enable ringback 672c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet startRingbackTone(); 673c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } else if (mState == Call.State.ALERTING) { 674c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet stopRingbackTone(); 67532d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung } 676c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet mState = newState; 677c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet updatePhoneState(); 678c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet notifyPreciseCallStateChanged(); 679c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 6802217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet } 681c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet 682c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet void onConnectionStateChanged(SipConnection conn) { 683c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet // this can be called back when a conf call is formed 684c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (SC_DBG) log("onConnectionStateChanged: conn=" + conn); 6852217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet if (mState != State.ACTIVE) { 6862217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet setState(conn.getState()); 6872217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet } 6882217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet } 6892217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet 6902217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet void onConnectionEnded(SipConnection conn) { 6912217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet // set state to DISCONNECTED only when all conns are disconnected 6922217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet if (SC_DBG) log("onConnectionEnded: conn=" + conn); 6932217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet if (mState != State.DISCONNECTED) { 6942217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet boolean allConnectionsDisconnected = true; 6952217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet if (SC_DBG) log("---check connections: " 6962217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet + mConnections.size()); 6972217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet for (Connection c : mConnections) { 6982217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet if (SC_DBG) log(" state=" + c.getState() + ": " 6992217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet + c); 7002217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet if (c.getState() != State.DISCONNECTED) { 7012217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet allConnectionsDisconnected = false; 7022217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet break; 7032217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet } 7046386ceb3bf25e442513224aaa45691dfe49562d9Jean-Luc Brouillet } 7056386ceb3bf25e442513224aaa45691dfe49562d9Jean-Luc Brouillet if (allConnectionsDisconnected) setState(State.DISCONNECTED); 7066386ceb3bf25e442513224aaa45691dfe49562d9Jean-Luc Brouillet } 7076386ceb3bf25e442513224aaa45691dfe49562d9Jean-Luc Brouillet notifyDisconnectP(conn); 7086386ceb3bf25e442513224aaa45691dfe49562d9Jean-Luc Brouillet } 7096386ceb3bf25e442513224aaa45691dfe49562d9Jean-Luc Brouillet 7106386ceb3bf25e442513224aaa45691dfe49562d9Jean-Luc Brouillet private AudioGroup getAudioGroup() { 7116386ceb3bf25e442513224aaa45691dfe49562d9Jean-Luc Brouillet if (mConnections.isEmpty()) return null; 7126386ceb3bf25e442513224aaa45691dfe49562d9Jean-Luc Brouillet return ((SipConnection) mConnections.get(0)).getAudioGroup(); 7132217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet } 7142217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet 7152217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet private void log(String s) { 7162217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet Rlog.d(SC_TAG, s); 7172217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet } 7182217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet } 7192217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet 7206386ceb3bf25e442513224aaa45691dfe49562d9Jean-Luc Brouillet private class SipConnection extends SipConnectionBase { 7216386ceb3bf25e442513224aaa45691dfe49562d9Jean-Luc Brouillet private static final String SCN_TAG = "SipConnection"; 7226386ceb3bf25e442513224aaa45691dfe49562d9Jean-Luc Brouillet private static final boolean SCN_DBG = true; 7236386ceb3bf25e442513224aaa45691dfe49562d9Jean-Luc Brouillet 7242217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet private SipCall mOwner; 7252217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet private SipAudioCall mSipAudioCall; 7262217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet private Call.State mState = Call.State.IDLE; 72732d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung private SipProfile mPeer; 72832d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung private boolean mIncoming = false; 72932d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung private String mOriginalNumber; // may be a PSTN number 7302217eb7b12e598e5b435a732207647918c171560Jean-Luc Brouillet 731c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet private SipAudioCallAdapter mAdapter = new SipAudioCallAdapter() { 73232d3520f388756d384fb1a130fd1142b8481f190I-Jui (Ray) Sung @Override 733c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet protected void onCallEnded(int cause) { 734c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet if (getDisconnectCause() != DisconnectCause.LOCAL) { 735c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet setDisconnectCause(cause); 736c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet } 737c5184e202ced435258adb2cfe2013570e7190954Jean-Luc Brouillet synchronized (SipPhone.class) { 738 setState(Call.State.DISCONNECTED); 739 SipAudioCall sipAudioCall = mSipAudioCall; 740 // FIXME: This goes null and is synchronized, but many uses aren't sync'd 741 mSipAudioCall = null; 742 String sessionState = (sipAudioCall == null) 743 ? "" 744 : (sipAudioCall.getState() + ", "); 745 if (SCN_DBG) log("[SipAudioCallAdapter] onCallEnded: " 746 + mPeer.getUriString() + ": " + sessionState 747 + "cause: " + getDisconnectCause() + ", on phone " 748 + getPhone()); 749 if (sipAudioCall != null) { 750 sipAudioCall.setListener(null); 751 sipAudioCall.close(); 752 } 753 mOwner.onConnectionEnded(SipConnection.this); 754 } 755 } 756 757 @Override 758 public void onCallEstablished(SipAudioCall call) { 759 onChanged(call); 760 // Race onChanged synchronized this isn't 761 if (mState == Call.State.ACTIVE) call.startAudio(); 762 } 763 764 @Override 765 public void onCallHeld(SipAudioCall call) { 766 onChanged(call); 767 // Race onChanged synchronized this isn't 768 if (mState == Call.State.HOLDING) call.startAudio(); 769 } 770 771 @Override 772 public void onChanged(SipAudioCall call) { 773 synchronized (SipPhone.class) { 774 Call.State newState = getCallStateFrom(call); 775 if (mState == newState) return; 776 if (newState == Call.State.INCOMING) { 777 setState(mOwner.getState()); // INCOMING or WAITING 778 } else { 779 if (mOwner == mRingingCall) { 780 if (mRingingCall.getState() == Call.State.WAITING) { 781 try { 782 switchHoldingAndActive(); 783 } catch (CallStateException e) { 784 // disconnect the call. 785 onCallEnded(DisconnectCause.LOCAL); 786 return; 787 } 788 } 789 mForegroundCall.switchWith(mRingingCall); 790 } 791 setState(newState); 792 } 793 mOwner.onConnectionStateChanged(SipConnection.this); 794 if (SCN_DBG) log("onChanged: " 795 + mPeer.getUriString() + ": " + mState 796 + " on phone " + getPhone()); 797 } 798 } 799 800 @Override 801 protected void onError(int cause) { 802 if (SCN_DBG) log("onError: " + cause); 803 onCallEnded(cause); 804 } 805 }; 806 807 public SipConnection(SipCall owner, SipProfile callee, 808 String originalNumber) { 809 super(originalNumber); 810 mOwner = owner; 811 mPeer = callee; 812 mOriginalNumber = originalNumber; 813 } 814 815 public SipConnection(SipCall owner, SipProfile callee) { 816 this(owner, callee, getUriString(callee)); 817 } 818 819 @Override 820 public String getCnapName() { 821 String displayName = mPeer.getDisplayName(); 822 return TextUtils.isEmpty(displayName) ? null 823 : displayName; 824 } 825 826 @Override 827 public int getNumberPresentation() { 828 return PhoneConstants.PRESENTATION_ALLOWED; 829 } 830 831 void initIncomingCall(SipAudioCall sipAudioCall, Call.State newState) { 832 setState(newState); 833 mSipAudioCall = sipAudioCall; 834 sipAudioCall.setListener(mAdapter); // call back to set state 835 mIncoming = true; 836 } 837 838 void acceptCall() throws CallStateException { 839 try { 840 mSipAudioCall.answerCall(TIMEOUT_ANSWER_CALL); 841 } catch (SipException e) { 842 throw new CallStateException("acceptCall(): " + e); 843 } 844 } 845 846 void changeOwner(SipCall owner) { 847 mOwner = owner; 848 } 849 850 AudioGroup getAudioGroup() { 851 if (mSipAudioCall == null) return null; 852 return mSipAudioCall.getAudioGroup(); 853 } 854 855 void dial() throws SipException { 856 setState(Call.State.DIALING); 857 mSipAudioCall = mSipManager.makeAudioCall(mProfile, mPeer, null, 858 TIMEOUT_MAKE_CALL); 859 mSipAudioCall.setListener(mAdapter); 860 } 861 862 void hold() throws CallStateException { 863 setState(Call.State.HOLDING); 864 try { 865 mSipAudioCall.holdCall(TIMEOUT_HOLD_CALL); 866 } catch (SipException e) { 867 throw new CallStateException("hold(): " + e); 868 } 869 } 870 871 void unhold(AudioGroup audioGroup) throws CallStateException { 872 mSipAudioCall.setAudioGroup(audioGroup); 873 setState(Call.State.ACTIVE); 874 try { 875 mSipAudioCall.continueCall(TIMEOUT_HOLD_CALL); 876 } catch (SipException e) { 877 throw new CallStateException("unhold(): " + e); 878 } 879 } 880 881 void setMute(boolean muted) { 882 if ((mSipAudioCall != null) && (muted != mSipAudioCall.isMuted())) { 883 if (SCN_DBG) log("setState: prev muted=" + !muted + " new muted=" + muted); 884 mSipAudioCall.toggleMute(); 885 } 886 } 887 888 boolean getMute() { 889 return (mSipAudioCall == null) ? false 890 : mSipAudioCall.isMuted(); 891 } 892 893 @Override 894 protected void setState(Call.State state) { 895 if (state == mState) return; 896 super.setState(state); 897 mState = state; 898 } 899 900 @Override 901 public Call.State getState() { 902 return mState; 903 } 904 905 @Override 906 public boolean isIncoming() { 907 return mIncoming; 908 } 909 910 @Override 911 public String getAddress() { 912 // Phone app uses this to query caller ID. Return the original dial 913 // number (which may be a PSTN number) instead of the peer's SIP 914 // URI. 915 return mOriginalNumber; 916 } 917 918 @Override 919 public SipCall getCall() { 920 return mOwner; 921 } 922 923 @Override 924 protected Phone getPhone() { 925 return mOwner.getPhone(); 926 } 927 928 @Override 929 public void hangup() throws CallStateException { 930 synchronized (SipPhone.class) { 931 if (SCN_DBG) log("hangup: conn=" + mPeer.getUriString() 932 + ": " + mState + ": on phone " 933 + getPhone().getPhoneName()); 934 if (!mState.isAlive()) return; 935 try { 936 SipAudioCall sipAudioCall = mSipAudioCall; 937 if (sipAudioCall != null) { 938 sipAudioCall.setListener(null); 939 sipAudioCall.endCall(); 940 } 941 } catch (SipException e) { 942 throw new CallStateException("hangup(): " + e); 943 } finally { 944 mAdapter.onCallEnded(((mState == Call.State.INCOMING) 945 || (mState == Call.State.WAITING)) 946 ? DisconnectCause.INCOMING_REJECTED 947 : DisconnectCause.LOCAL); 948 } 949 } 950 } 951 952 @Override 953 public void separate() throws CallStateException { 954 synchronized (SipPhone.class) { 955 SipCall call = (getPhone() == SipPhone.this) 956 ? (SipCall) getBackgroundCall() 957 : (SipCall) getForegroundCall(); 958 if (call.getState() != Call.State.IDLE) { 959 throw new CallStateException( 960 "cannot put conn back to a call in non-idle state: " 961 + call.getState()); 962 } 963 if (SCN_DBG) log("separate: conn=" 964 + mPeer.getUriString() + " from " + mOwner + " back to " 965 + call); 966 967 // separate the AudioGroup and connection from the original call 968 Phone originalPhone = getPhone(); 969 AudioGroup audioGroup = call.getAudioGroup(); // may be null 970 call.add(this); 971 mSipAudioCall.setAudioGroup(audioGroup); 972 973 // put the original call to bg; and the separated call becomes 974 // fg if it was in bg 975 originalPhone.switchHoldingAndActive(); 976 977 // start audio and notify the phone app of the state change 978 call = (SipCall) getForegroundCall(); 979 mSipAudioCall.startAudio(); 980 call.onConnectionStateChanged(this); 981 } 982 } 983 984 private void log(String s) { 985 Rlog.d(SCN_TAG, s); 986 } 987 } 988 989 private abstract class SipAudioCallAdapter extends SipAudioCall.Listener { 990 private static final String SACA_TAG = "SipAudioCallAdapter"; 991 private static final boolean SACA_DBG = true; 992 /** Call ended with cause defined in {@link DisconnectCause}. */ 993 protected abstract void onCallEnded(int cause); 994 /** Call failed with cause defined in {@link DisconnectCause}. */ 995 protected abstract void onError(int cause); 996 997 @Override 998 public void onCallEnded(SipAudioCall call) { 999 if (SACA_DBG) log("onCallEnded: call=" + call); 1000 onCallEnded(call.isInCall() 1001 ? DisconnectCause.NORMAL 1002 : DisconnectCause.INCOMING_MISSED); 1003 } 1004 1005 @Override 1006 public void onCallBusy(SipAudioCall call) { 1007 if (SACA_DBG) log("onCallBusy: call=" + call); 1008 onCallEnded(DisconnectCause.BUSY); 1009 } 1010 1011 @Override 1012 public void onError(SipAudioCall call, int errorCode, 1013 String errorMessage) { 1014 if (SACA_DBG) { 1015 log("onError: call=" + call + " code="+ SipErrorCode.toString(errorCode) 1016 + ": " + errorMessage); 1017 } 1018 switch (errorCode) { 1019 case SipErrorCode.SERVER_UNREACHABLE: 1020 onError(DisconnectCause.SERVER_UNREACHABLE); 1021 break; 1022 case SipErrorCode.PEER_NOT_REACHABLE: 1023 onError(DisconnectCause.NUMBER_UNREACHABLE); 1024 break; 1025 case SipErrorCode.INVALID_REMOTE_URI: 1026 onError(DisconnectCause.INVALID_NUMBER); 1027 break; 1028 case SipErrorCode.TIME_OUT: 1029 case SipErrorCode.TRANSACTION_TERMINTED: 1030 onError(DisconnectCause.TIMED_OUT); 1031 break; 1032 case SipErrorCode.DATA_CONNECTION_LOST: 1033 onError(DisconnectCause.LOST_SIGNAL); 1034 break; 1035 case SipErrorCode.INVALID_CREDENTIALS: 1036 onError(DisconnectCause.INVALID_CREDENTIALS); 1037 break; 1038 case SipErrorCode.CROSS_DOMAIN_AUTHENTICATION: 1039 onError(DisconnectCause.OUT_OF_NETWORK); 1040 break; 1041 case SipErrorCode.SERVER_ERROR: 1042 onError(DisconnectCause.SERVER_ERROR); 1043 break; 1044 case SipErrorCode.SOCKET_ERROR: 1045 case SipErrorCode.CLIENT_ERROR: 1046 default: 1047 onError(DisconnectCause.ERROR_UNSPECIFIED); 1048 } 1049 } 1050 1051 private void log(String s) { 1052 Rlog.d(SACA_TAG, s); 1053 } 1054 } 1055} 1056