1ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang/* 2ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang * Copyright (C) 2010 The Android Open Source Project 3ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang * 4ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang * Licensed under the Apache License, Version 2.0 (the "License"); 5ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang * you may not use this file except in compliance with the License. 6ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang * You may obtain a copy of the License at 7ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang * 8ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang * http://www.apache.org/licenses/LICENSE-2.0 9ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang * 10ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang * Unless required by applicable law or agreed to in writing, software 11ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang * distributed under the License is distributed on an "AS IS" BASIS, 12ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang * See the License for the specific language governing permissions and 14ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang * limitations under the License. 15ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang */ 16ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 17ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wangpackage com.android.internal.telephony.sip; 18ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 19ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wangimport android.content.Context; 201d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyanimport android.media.AudioManager; 21ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wangimport android.net.rtp.AudioGroup; 22ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wangimport android.net.sip.SipAudioCall; 23903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyanimport android.net.sip.SipErrorCode; 2425b52a2f97df112c2836972d0b6d9a4c7a9c4a4eHung-ying Tyanimport android.net.sip.SipException; 25ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wangimport android.net.sip.SipManager; 26ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wangimport android.net.sip.SipProfile; 2784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport android.net.sip.SipSession; 28ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wangimport android.os.AsyncResult; 29ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wangimport android.os.Message; 30ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wangimport android.telephony.PhoneNumberUtils; 31ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wangimport android.telephony.ServiceState; 32538e58fc757b0d10672235bc17b1380854845139Hung-ying Tyanimport android.text.TextUtils; 33ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wangimport android.util.Log; 34ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 35ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wangimport com.android.internal.telephony.Call; 36ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wangimport com.android.internal.telephony.CallStateException; 37ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wangimport com.android.internal.telephony.Connection; 38ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wangimport com.android.internal.telephony.Phone; 39ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wangimport com.android.internal.telephony.PhoneNotifier; 40ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 41ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wangimport java.text.ParseException; 42ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wangimport java.util.List; 43b5c72ead014a509c0f84884d1f2dac1ff9deec8eMagnus Strandbergimport java.util.regex.Pattern; 44ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 45ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang/** 46ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang * {@hide} 47ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang */ 48ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wangpublic class SipPhone extends SipPhoneBase { 49ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private static final String LOG_TAG = "SipPhone"; 50aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan private static final boolean DEBUG = true; 51194bbcce9ba15634500f542b9ea017b2cf154b45Hung-ying Tyan private static final int TIMEOUT_MAKE_CALL = 15; // in seconds 52194bbcce9ba15634500f542b9ea017b2cf154b45Hung-ying Tyan private static final int TIMEOUT_ANSWER_CALL = 8; // in seconds 53194bbcce9ba15634500f542b9ea017b2cf154b45Hung-ying Tyan private static final int TIMEOUT_HOLD_CALL = 15; // in seconds 54ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 55ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang // A call that is ringing or (call) waiting 56ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private SipCall ringingCall = new SipCall(); 57ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private SipCall foregroundCall = new SipCall(); 58ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private SipCall backgroundCall = new SipCall(); 59ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 60ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private SipManager mSipManager; 61ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private SipProfile mProfile; 62ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 63ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang SipPhone (Context context, PhoneNotifier notifier, SipProfile profile) { 64ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang super(context, notifier); 65ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 66aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan if (DEBUG) Log.d(LOG_TAG, "new SipPhone: " + profile.getUriString()); 67ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang ringingCall = new SipCall(); 68ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang foregroundCall = new SipCall(); 69ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang backgroundCall = new SipCall(); 70ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang mProfile = profile; 7184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mSipManager = SipManager.newInstance(context); 72ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 73ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 7488e590fb370d80d863417aae9330c8c2218f3175Chung-yih Wang @Override 7588e590fb370d80d863417aae9330c8c2218f3175Chung-yih Wang public boolean equals(Object o) { 7688e590fb370d80d863417aae9330c8c2218f3175Chung-yih Wang if (o == this) return true; 7788e590fb370d80d863417aae9330c8c2218f3175Chung-yih Wang if (!(o instanceof SipPhone)) return false; 7888e590fb370d80d863417aae9330c8c2218f3175Chung-yih Wang SipPhone that = (SipPhone) o; 7988e590fb370d80d863417aae9330c8c2218f3175Chung-yih Wang return mProfile.getUriString().equals(that.mProfile.getUriString()); 8088e590fb370d80d863417aae9330c8c2218f3175Chung-yih Wang } 8188e590fb370d80d863417aae9330c8c2218f3175Chung-yih Wang 82ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public String getPhoneName() { 833294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan return "SIP:" + getUriString(mProfile); 84ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 85ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 86f57324cf4f82947296f4d1acb9df1f3c9c03134eChung-yih Wang public String getSipUri() { 87f57324cf4f82947296f4d1acb9df1f3c9c03134eChung-yih Wang return mProfile.getUriString(); 88f57324cf4f82947296f4d1acb9df1f3c9c03134eChung-yih Wang } 89f57324cf4f82947296f4d1acb9df1f3c9c03134eChung-yih Wang 90b12baad9357c6e6aec1f7d84fd041c54fe963407Chung-yih Wang public boolean equals(SipPhone phone) { 91b6264a8795ed9469c80727123e3cafda1b07eda3Chung-yih Wang return getSipUri().equals(phone.getSipUri()); 92b12baad9357c6e6aec1f7d84fd041c54fe963407Chung-yih Wang } 93b12baad9357c6e6aec1f7d84fd041c54fe963407Chung-yih Wang 94ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public boolean canTake(Object incomingCall) { 95ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang synchronized (SipPhone.class) { 96ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (!(incomingCall instanceof SipAudioCall)) return false; 97ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (ringingCall.getState().isAlive()) return false; 98ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 99ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang // FIXME: is it true that we cannot take any incoming call if 100ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang // both foreground and background are active 101ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (foregroundCall.getState().isAlive() 102ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang && backgroundCall.getState().isAlive()) { 103ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return false; 104ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 105ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 1060e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan try { 1070e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan SipAudioCall sipAudioCall = (SipAudioCall) incomingCall; 108aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan if (DEBUG) Log.d(LOG_TAG, "+++ taking call from: " 1090e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan + sipAudioCall.getPeerProfile().getUriString()); 1100e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan String localUri = sipAudioCall.getLocalProfile().getUriString(); 1110e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan if (localUri.equals(mProfile.getUriString())) { 1120e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan boolean makeCallWait = foregroundCall.getState().isAlive(); 1130e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan ringingCall.initIncomingCall(sipAudioCall, makeCallWait); 1140e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan if (sipAudioCall.getState() 1150e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan != SipSession.State.INCOMING_CALL) { 1160e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan // Peer cancelled the call! 117aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan if (DEBUG) Log.d(LOG_TAG, " call cancelled !!"); 1180e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan ringingCall.reset(); 1190e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan } 1200e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan return true; 1210e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan } 1220e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan } catch (Exception e) { 1230e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan // Peer may cancel the call at any time during the time we hook 1240e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan // up ringingCall with sipAudioCall. Clean up ringingCall when 1250e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan // that happens. 1260e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan ringingCall.reset(); 127ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 128ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return false; 129ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 130ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 131ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 132ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void acceptCall() throws CallStateException { 133ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang synchronized (SipPhone.class) { 1349779b714f4035642b87cbb7ef6cd8ac32848c930Chung-yih Wang if ((ringingCall.getState() == Call.State.INCOMING) || 1359779b714f4035642b87cbb7ef6cd8ac32848c930Chung-yih Wang (ringingCall.getState() == Call.State.WAITING)) { 136aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan if (DEBUG) Log.d(LOG_TAG, "acceptCall"); 137ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang // Always unmute when answering a new call 1381d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan ringingCall.setMute(false); 1399779b714f4035642b87cbb7ef6cd8ac32848c930Chung-yih Wang ringingCall.acceptCall(); 140ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } else { 141ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang throw new CallStateException("phone not ringing"); 142ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 143ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 144ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 145ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 146ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void rejectCall() throws CallStateException { 147ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang synchronized (SipPhone.class) { 148ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (ringingCall.getState().isRinging()) { 149aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan if (DEBUG) Log.d(LOG_TAG, "rejectCall"); 150ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang ringingCall.rejectCall(); 151ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } else { 152ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang throw new CallStateException("phone not ringing"); 153ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 154ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 155ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 156ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 157ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public Connection dial(String dialString) throws CallStateException { 158ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang synchronized (SipPhone.class) { 159ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return dialInternal(dialString); 160ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 161ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 162ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 163ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private Connection dialInternal(String dialString) 164ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang throws CallStateException { 165ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang clearDisconnected(); 166ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 167ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (!canDial()) { 168ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang throw new CallStateException("cannot dial in current state"); 169ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 170ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (foregroundCall.getState() == SipCall.State.ACTIVE) { 171ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang switchHoldingAndActive(); 172ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 173ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (foregroundCall.getState() != SipCall.State.IDLE) { 174ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang //we should have failed in !canDial() above before we get here 175ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang throw new CallStateException("cannot dial in current state"); 176ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 177ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 1781d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan foregroundCall.setMute(false); 179ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang try { 180ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang Connection c = foregroundCall.dial(dialString); 181ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return c; 182ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } catch (SipException e) { 183ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang Log.e(LOG_TAG, "dial()", e); 184ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang throw new CallStateException("dial error: " + e); 185ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 186ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 187ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 188ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void switchHoldingAndActive() throws CallStateException { 189aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan if (DEBUG) Log.d(LOG_TAG, " ~~~~~~ switch fg and bg"); 190ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang synchronized (SipPhone.class) { 191ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang foregroundCall.switchWith(backgroundCall); 192ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (backgroundCall.getState().isAlive()) backgroundCall.hold(); 193ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (foregroundCall.getState().isAlive()) foregroundCall.unhold(); 194ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 195ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 196ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 197ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public boolean canConference() { 198ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return true; 199ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 200ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 201ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void conference() throws CallStateException { 2023294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan synchronized (SipPhone.class) { 2033294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan if ((foregroundCall.getState() != SipCall.State.ACTIVE) 2043294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan || (foregroundCall.getState() != SipCall.State.ACTIVE)) { 2053294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan throw new CallStateException("wrong state to merge calls: fg=" 2063294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan + foregroundCall.getState() + ", bg=" 2073294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan + backgroundCall.getState()); 2083294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan } 2093294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan foregroundCall.merge(backgroundCall); 2108eac20eacd088793547c56e14d602b28d62fb278Hung-ying Tyan } 2118eac20eacd088793547c56e14d602b28d62fb278Hung-ying Tyan } 2128eac20eacd088793547c56e14d602b28d62fb278Hung-ying Tyan 2138eac20eacd088793547c56e14d602b28d62fb278Hung-ying Tyan public void conference(Call that) throws CallStateException { 2143294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan synchronized (SipPhone.class) { 2153294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan if (!(that instanceof SipCall)) { 2163294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan throw new CallStateException("expect " + SipCall.class 2173294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan + ", cannot merge with " + that.getClass()); 2183294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan } 2193294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan foregroundCall.merge((SipCall) that); 2208eac20eacd088793547c56e14d602b28d62fb278Hung-ying Tyan } 221ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 222ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 223ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public boolean canTransfer() { 224ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return false; 225ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 226ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 227ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void explicitCallTransfer() throws CallStateException { 228ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang //mCT.explicitCallTransfer(); 229ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 230ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 231ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void clearDisconnected() { 2323294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan synchronized (SipPhone.class) { 2333294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan ringingCall.clearDisconnected(); 2343294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan foregroundCall.clearDisconnected(); 2353294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan backgroundCall.clearDisconnected(); 236ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 2373294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan updatePhoneState(); 2383294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan notifyPreciseCallStateChanged(); 2393294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan } 240ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 241ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 242ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void sendDtmf(char c) { 243ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (!PhoneNumberUtils.is12Key(c)) { 244ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang Log.e(LOG_TAG, 245ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang "sendDtmf called with invalid character '" + c + "'"); 246ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } else if (foregroundCall.getState().isAlive()) { 2473294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan synchronized (SipPhone.class) { 2483294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan foregroundCall.sendDtmf(c); 2493294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan } 250ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 251ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 252ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 253ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void startDtmf(char c) { 254ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (!PhoneNumberUtils.is12Key(c)) { 255ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang Log.e(LOG_TAG, 256ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang "startDtmf called with invalid character '" + c + "'"); 257ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } else { 258ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang sendDtmf(c); 259ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 260ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 261ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 262ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void stopDtmf() { 263ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang // no op 264ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 265ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 266ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void sendBurstDtmf(String dtmfString) { 267ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang Log.e(LOG_TAG, "[SipPhone] sendBurstDtmf() is a CDMA method"); 268ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 269ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 270ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void getOutgoingCallerIdDisplay(Message onComplete) { 271ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang // FIXME: what to reply? 272ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang AsyncResult.forMessage(onComplete, null, null); 273ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang onComplete.sendToTarget(); 274ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 275ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 276ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, 277ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang Message onComplete) { 278ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang // FIXME: what's this for SIP? 279ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang AsyncResult.forMessage(onComplete, null, null); 280ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang onComplete.sendToTarget(); 281ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 282ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 283ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void getCallWaiting(Message onComplete) { 284ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang // FIXME: what to reply? 285ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang AsyncResult.forMessage(onComplete, null, null); 286ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang onComplete.sendToTarget(); 287ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 288ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 289ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void setCallWaiting(boolean enable, Message onComplete) { 290ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang // FIXME: what to reply? 291ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang Log.e(LOG_TAG, "call waiting not supported"); 292ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 293ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 29423f21600d0927365e5e7bdc4e566ba52101301b4Hung-ying Tyan @Override 29523f21600d0927365e5e7bdc4e566ba52101301b4Hung-ying Tyan public void setEchoSuppressionEnabled(boolean enabled) { 2961d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan // TODO: Remove the enabled argument. We should check the speakerphone 2971d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan // state with AudioManager instead of keeping a state here so the 2981d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan // method with a state argument is redundant. Also rename the method 2991d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan // to something like onSpeaerphoneStateChanged(). Echo suppression may 3001d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan // not be available on every device. 30123f21600d0927365e5e7bdc4e566ba52101301b4Hung-ying Tyan synchronized (SipPhone.class) { 3021d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan foregroundCall.setAudioGroupMode(); 30323f21600d0927365e5e7bdc4e566ba52101301b4Hung-ying Tyan } 30423f21600d0927365e5e7bdc4e566ba52101301b4Hung-ying Tyan } 30523f21600d0927365e5e7bdc4e566ba52101301b4Hung-ying Tyan 306ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void setMute(boolean muted) { 3073294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan synchronized (SipPhone.class) { 3083294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan foregroundCall.setMute(muted); 3093294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan } 310ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 311ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 312ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public boolean getMute() { 31365a7f147deb02f728959eb05913a2d6ce53dea1cHung-ying Tyan return (foregroundCall.getState().isAlive() 31465a7f147deb02f728959eb05913a2d6ce53dea1cHung-ying Tyan ? foregroundCall.getMute() 31565a7f147deb02f728959eb05913a2d6ce53dea1cHung-ying Tyan : backgroundCall.getMute()); 316ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 317ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 318ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public Call getForegroundCall() { 319ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return foregroundCall; 320ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 321ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 322ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public Call getBackgroundCall() { 323ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return backgroundCall; 324ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 325ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 326ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public Call getRingingCall() { 327ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return ringingCall; 328ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 329ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 330ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public ServiceState getServiceState() { 331ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang // FIXME: we may need to provide this when data connectivity is lost 332ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang // or when server is down 333ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return super.getServiceState(); 334ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 335ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 336ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private String getUriString(SipProfile p) { 337ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang // SipProfile.getUriString() may contain "SIP:" and port 338ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return p.getUserName() + "@" + getSipDomain(p); 339ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 340ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 341ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private String getSipDomain(SipProfile p) { 342ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang String domain = p.getSipDomain(); 343ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang // TODO: move this to SipProfile 344ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (domain.endsWith(":5060")) { 345ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return domain.substring(0, domain.length() - 5); 346ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } else { 347ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return domain; 348ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 349ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 350ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 351ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private class SipCall extends SipCallBase { 3520e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan void reset() { 3530e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan connections.clear(); 3540e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan setState(Call.State.IDLE); 3550e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan } 3560e412304813ccd3a3fb6a643836e4f0922d1dc44Hung-ying Tyan 357ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang void switchWith(SipCall that) { 358ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang synchronized (SipPhone.class) { 359ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang SipCall tmp = new SipCall(); 360ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang tmp.takeOver(this); 361ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang this.takeOver(that); 362ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang that.takeOver(tmp); 363ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 364ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 365ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 366ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private void takeOver(SipCall that) { 367ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang connections = that.connections; 368ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang state = that.state; 369ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang for (Connection c : connections) { 370ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang ((SipConnection) c).changeOwner(this); 371ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 372ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 373ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 374ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang @Override 375ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public Phone getPhone() { 376ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return SipPhone.this; 377ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 378ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 379ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang @Override 380ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public List<Connection> getConnections() { 381ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang synchronized (SipPhone.class) { 382ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang // FIXME should return Collections.unmodifiableList(); 383ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return connections; 384ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 385ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 386ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 3871d1583573d2099756bbbeef48d97c280edc393e0Hung-ying Tyan Connection dial(String originalNumber) throws SipException { 3881d1583573d2099756bbbeef48d97c280edc393e0Hung-ying Tyan String calleeSipUri = originalNumber; 389ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (!calleeSipUri.contains("@")) { 390b5c72ead014a509c0f84884d1f2dac1ff9deec8eMagnus Strandberg String replaceStr = Pattern.quote(mProfile.getUserName() + "@"); 391b5c72ead014a509c0f84884d1f2dac1ff9deec8eMagnus Strandberg calleeSipUri = mProfile.getUriString().replaceFirst(replaceStr, 392f053292d7a46c30abbe6f12ca04dbc03ec964d80Chung-yih Wang calleeSipUri + "@"); 393ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 394ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang try { 395ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang SipProfile callee = 396ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang new SipProfile.Builder(calleeSipUri).build(); 397f5201ab71ff4d104265ab126e86afc6b81da8011Hung-ying Tyan SipConnection c = new SipConnection(this, callee, 398f5201ab71ff4d104265ab126e86afc6b81da8011Hung-ying Tyan originalNumber); 399ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang c.dial(); 4008d1b2a17d9935819ec96f1b5fca0e9945f564eaaHung-ying Tyan connections.add(c); 401ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang setState(Call.State.DIALING); 402ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return c; 403ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } catch (ParseException e) { 404ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang throw new SipException("dial", e); 405ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 406ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 407ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 408ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang @Override 409ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void hangup() throws CallStateException { 4103294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan synchronized (SipPhone.class) { 411c508e8468f54164e7c9729e3800e2559b803cef1Hung-ying Tyan if (state.isAlive()) { 412aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan if (DEBUG) Log.d(LOG_TAG, "hang up call: " + getState() 413aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan + ": " + this + " on phone " + getPhone()); 414421c34c162098efe870574844a7ee49812bbb929Hung-ying Tyan setState(State.DISCONNECTING); 4152b4f5cfd9be5ceffc4745a45736e067a475a4dffHung-ying Tyan CallStateException excp = null; 4162b4f5cfd9be5ceffc4745a45736e067a475a4dffHung-ying Tyan for (Connection c : connections) { 4172b4f5cfd9be5ceffc4745a45736e067a475a4dffHung-ying Tyan try { 4182b4f5cfd9be5ceffc4745a45736e067a475a4dffHung-ying Tyan c.hangup(); 4192b4f5cfd9be5ceffc4745a45736e067a475a4dffHung-ying Tyan } catch (CallStateException e) { 4202b4f5cfd9be5ceffc4745a45736e067a475a4dffHung-ying Tyan excp = e; 4212b4f5cfd9be5ceffc4745a45736e067a475a4dffHung-ying Tyan } 4223294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan } 4232b4f5cfd9be5ceffc4745a45736e067a475a4dffHung-ying Tyan if (excp != null) throw excp; 4242b4f5cfd9be5ceffc4745a45736e067a475a4dffHung-ying Tyan } else { 425aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan if (DEBUG) Log.d(LOG_TAG, "hang up dead call: " + getState() 426aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan + ": " + this + " on phone " + getPhone()); 427ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 428ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 429ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 430ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 431ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang void initIncomingCall(SipAudioCall sipAudioCall, boolean makeCallWait) { 432ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang SipProfile callee = sipAudioCall.getPeerProfile(); 433d07833f54b6e8e361b666ae16efa15fdf60159deDavid Brown SipConnection c = new SipConnection(this, callee); 434ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang connections.add(c); 435ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 436ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang Call.State newState = makeCallWait ? State.WAITING : State.INCOMING; 437ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang c.initIncomingCall(sipAudioCall, newState); 438ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 439ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang setState(newState); 440ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang notifyNewRingingConnectionP(c); 441ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 442ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 443ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang void rejectCall() throws CallStateException { 444ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang hangup(); 445ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 446ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 447ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang void acceptCall() throws CallStateException { 4489779b714f4035642b87cbb7ef6cd8ac32848c930Chung-yih Wang if (this != ringingCall) { 4499779b714f4035642b87cbb7ef6cd8ac32848c930Chung-yih Wang throw new CallStateException("acceptCall() in a non-ringing call"); 450ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 451ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (connections.size() != 1) { 452ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang throw new CallStateException("acceptCall() in a conf call"); 453ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 454ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang ((SipConnection) connections.get(0)).acceptCall(); 455ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 456ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 4571d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan private boolean isSpeakerOn() { 4581d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan return ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)) 4591d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan .isSpeakerphoneOn(); 4601d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan } 4611d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan 4621d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan void setAudioGroupMode() { 4633294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan AudioGroup audioGroup = getAudioGroup(); 4641d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan if (audioGroup == null) return; 4651d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan int mode = audioGroup.getMode(); 4661d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan if (state == State.HOLDING) { 4673294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan audioGroup.setMode(AudioGroup.MODE_ON_HOLD); 4681d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan } else if (getMute()) { 4691d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan audioGroup.setMode(AudioGroup.MODE_MUTED); 4701d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan } else if (isSpeakerOn()) { 4711d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan audioGroup.setMode(AudioGroup.MODE_ECHO_SUPPRESSION); 4721d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan } else { 4731d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan audioGroup.setMode(AudioGroup.MODE_NORMAL); 4743294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan } 4751d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan if (DEBUG) Log.d(LOG_TAG, String.format( 4761d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan "audioGroup mode change: %d --> %d", mode, 4771d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan audioGroup.getMode())); 4781d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan } 4791d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan 4801d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan void hold() throws CallStateException { 4811d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan setState(State.HOLDING); 482ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang for (Connection c : connections) ((SipConnection) c).hold(); 4831d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan setAudioGroupMode(); 484ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 485ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 486ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang void unhold() throws CallStateException { 487ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang setState(State.ACTIVE); 4883294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan AudioGroup audioGroup = new AudioGroup(); 4893294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan for (Connection c : connections) { 4903294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan ((SipConnection) c).unhold(audioGroup); 4913294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan } 4921d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan setAudioGroupMode(); 493ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 494ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 495ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang void setMute(boolean muted) { 4961d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan for (Connection c : connections) { 4971d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan ((SipConnection) c).setMute(muted); 4981d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan } 499ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 500ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 501ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang boolean getMute() { 5021d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan return connections.isEmpty() 5031d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan ? false 5041d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan : ((SipConnection) connections.get(0)).getMute(); 505ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 506ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 5078eac20eacd088793547c56e14d602b28d62fb278Hung-ying Tyan void merge(SipCall that) throws CallStateException { 5083294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan AudioGroup audioGroup = getAudioGroup(); 5096037a056ea0dda27a286ddcb527b323b58a1c7c7Hung-ying Tyan 5106037a056ea0dda27a286ddcb527b323b58a1c7c7Hung-ying Tyan // copy to an array to avoid concurrent modification as connections 5116037a056ea0dda27a286ddcb527b323b58a1c7c7Hung-ying Tyan // in that.connections will be removed in add(SipConnection). 5126037a056ea0dda27a286ddcb527b323b58a1c7c7Hung-ying Tyan Connection[] cc = that.connections.toArray( 5136037a056ea0dda27a286ddcb527b323b58a1c7c7Hung-ying Tyan new Connection[that.connections.size()]); 5146037a056ea0dda27a286ddcb527b323b58a1c7c7Hung-ying Tyan for (Connection c : cc) { 5158eac20eacd088793547c56e14d602b28d62fb278Hung-ying Tyan SipConnection conn = (SipConnection) c; 5163294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan add(conn); 5173294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan if (conn.getState() == Call.State.HOLDING) { 5183294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan conn.unhold(audioGroup); 5193294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan } 5208eac20eacd088793547c56e14d602b28d62fb278Hung-ying Tyan } 5218eac20eacd088793547c56e14d602b28d62fb278Hung-ying Tyan that.setState(Call.State.IDLE); 5228eac20eacd088793547c56e14d602b28d62fb278Hung-ying Tyan } 5238eac20eacd088793547c56e14d602b28d62fb278Hung-ying Tyan 5243294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan private void add(SipConnection conn) { 5253294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan SipCall call = conn.getCall(); 5263294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan if (call == this) return; 5273294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan if (call != null) call.connections.remove(conn); 5283294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan 5293294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan connections.add(conn); 5303294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan conn.changeOwner(this); 5313294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan } 5323294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan 533ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang void sendDtmf(char c) { 534ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang AudioGroup audioGroup = getAudioGroup(); 535ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (audioGroup == null) return; 536ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang audioGroup.sendDtmf(convertDtmf(c)); 537ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 538ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 539ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private int convertDtmf(char c) { 540ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang int code = c - '0'; 541ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if ((code < 0) || (code > 9)) { 542ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang switch (c) { 543ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang case '*': return 10; 544ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang case '#': return 11; 545ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang case 'A': return 12; 546ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang case 'B': return 13; 547ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang case 'C': return 14; 548ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang case 'D': return 15; 549ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang default: 550ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang throw new IllegalArgumentException( 551ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang "invalid DTMF char: " + (int) c); 552ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 553ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 554ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return code; 555ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 556ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 557ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang @Override 558ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang protected void setState(State newState) { 559ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (state != newState) { 560aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan if (DEBUG) Log.v(LOG_TAG, "+***+ call state changed: " + state 561ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang + " --> " + newState + ": " + this + ": on phone " 562ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang + getPhone() + " " + connections.size()); 563ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 564ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (newState == Call.State.ALERTING) { 565ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang state = newState; // need in ALERTING to enable ringback 566ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang SipPhone.this.startRingbackTone(); 567ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } else if (state == Call.State.ALERTING) { 568ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang SipPhone.this.stopRingbackTone(); 569ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 570ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang state = newState; 571ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang updatePhoneState(); 572ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang notifyPreciseCallStateChanged(); 573ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 574ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 575ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 576ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang void onConnectionStateChanged(SipConnection conn) { 577ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang // this can be called back when a conf call is formed 578ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (state != State.ACTIVE) { 579ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang setState(conn.getState()); 580ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 581ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 582ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 583ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang void onConnectionEnded(SipConnection conn) { 584ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang // set state to DISCONNECTED only when all conns are disconnected 585ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (state != State.DISCONNECTED) { 586ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang boolean allConnectionsDisconnected = true; 587aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan if (DEBUG) Log.d(LOG_TAG, "---check connections: " 588025a39af346f39743c1e384b9000ce1baee36562Hung-ying Tyan + connections.size()); 589ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang for (Connection c : connections) { 590aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan if (DEBUG) Log.d(LOG_TAG, " state=" + c.getState() + ": " 591aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan + c); 592ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (c.getState() != State.DISCONNECTED) { 593ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang allConnectionsDisconnected = false; 594ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang break; 595ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 596ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 597ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (allConnectionsDisconnected) setState(State.DISCONNECTED); 598ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 599ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang notifyDisconnectP(conn); 600ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 601ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 602ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private AudioGroup getAudioGroup() { 603ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (connections.isEmpty()) return null; 604ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return ((SipConnection) connections.get(0)).getAudioGroup(); 605ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 606ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 607ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 608ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private class SipConnection extends SipConnectionBase { 609ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private SipCall mOwner; 610ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private SipAudioCall mSipAudioCall; 611ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private Call.State mState = Call.State.IDLE; 612ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private SipProfile mPeer; 613f5201ab71ff4d104265ab126e86afc6b81da8011Hung-ying Tyan private String mOriginalNumber; // may be a PSTN number 614ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private boolean mIncoming = false; 615ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 616ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private SipAudioCallAdapter mAdapter = new SipAudioCallAdapter() { 617ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang @Override 618ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang protected void onCallEnded(DisconnectCause cause) { 619ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (getDisconnectCause() != DisconnectCause.LOCAL) { 620ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang setDisconnectCause(cause); 621ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 622ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang synchronized (SipPhone.class) { 623ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang setState(Call.State.DISCONNECTED); 624421c34c162098efe870574844a7ee49812bbb929Hung-ying Tyan SipAudioCall sipAudioCall = mSipAudioCall; 625421c34c162098efe870574844a7ee49812bbb929Hung-ying Tyan mSipAudioCall = null; 626421c34c162098efe870574844a7ee49812bbb929Hung-ying Tyan String sessionState = (sipAudioCall == null) 627421c34c162098efe870574844a7ee49812bbb929Hung-ying Tyan ? "" 628421c34c162098efe870574844a7ee49812bbb929Hung-ying Tyan : (sipAudioCall.getState() + ", "); 629aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan if (DEBUG) Log.d(LOG_TAG, "--- connection ended: " 630421c34c162098efe870574844a7ee49812bbb929Hung-ying Tyan + mPeer.getUriString() + ": " + sessionState 631421c34c162098efe870574844a7ee49812bbb929Hung-ying Tyan + "cause: " + getDisconnectCause() + ", on phone " 632ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang + getPhone()); 633421c34c162098efe870574844a7ee49812bbb929Hung-ying Tyan if (sipAudioCall != null) { 634421c34c162098efe870574844a7ee49812bbb929Hung-ying Tyan sipAudioCall.setListener(null); 635421c34c162098efe870574844a7ee49812bbb929Hung-ying Tyan sipAudioCall.close(); 636421c34c162098efe870574844a7ee49812bbb929Hung-ying Tyan } 637421c34c162098efe870574844a7ee49812bbb929Hung-ying Tyan mOwner.onConnectionEnded(SipConnection.this); 638ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 639ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 640ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 641ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang @Override 642bd2294204e3edaede3fe81eb9b11c05c4fafe627Chung-yih Wang public void onCallEstablished(SipAudioCall call) { 643bd2294204e3edaede3fe81eb9b11c05c4fafe627Chung-yih Wang onChanged(call); 644245475925eff61ee76bde58de69253a889e39d0aChung-yih Wang if (mState == Call.State.ACTIVE) call.startAudio(); 645bd2294204e3edaede3fe81eb9b11c05c4fafe627Chung-yih Wang } 646bd2294204e3edaede3fe81eb9b11c05c4fafe627Chung-yih Wang 647bd2294204e3edaede3fe81eb9b11c05c4fafe627Chung-yih Wang @Override 648bd2294204e3edaede3fe81eb9b11c05c4fafe627Chung-yih Wang public void onCallHeld(SipAudioCall call) { 649bd2294204e3edaede3fe81eb9b11c05c4fafe627Chung-yih Wang onChanged(call); 650245475925eff61ee76bde58de69253a889e39d0aChung-yih Wang if (mState == Call.State.HOLDING) call.startAudio(); 651bd2294204e3edaede3fe81eb9b11c05c4fafe627Chung-yih Wang } 652bd2294204e3edaede3fe81eb9b11c05c4fafe627Chung-yih Wang 653bd2294204e3edaede3fe81eb9b11c05c4fafe627Chung-yih Wang @Override 654ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void onChanged(SipAudioCall call) { 655ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang synchronized (SipPhone.class) { 656ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang Call.State newState = getCallStateFrom(call); 657ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (mState == newState) return; 658ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (newState == Call.State.INCOMING) { 659ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang setState(mOwner.getState()); // INCOMING or WAITING 660ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } else { 6619779b714f4035642b87cbb7ef6cd8ac32848c930Chung-yih Wang if (mOwner == ringingCall) { 6629779b714f4035642b87cbb7ef6cd8ac32848c930Chung-yih Wang if (ringingCall.getState() == Call.State.WAITING) { 6639779b714f4035642b87cbb7ef6cd8ac32848c930Chung-yih Wang try { 6649779b714f4035642b87cbb7ef6cd8ac32848c930Chung-yih Wang switchHoldingAndActive(); 6659779b714f4035642b87cbb7ef6cd8ac32848c930Chung-yih Wang } catch (CallStateException e) { 6669779b714f4035642b87cbb7ef6cd8ac32848c930Chung-yih Wang // disconnect the call. 6679779b714f4035642b87cbb7ef6cd8ac32848c930Chung-yih Wang onCallEnded(DisconnectCause.LOCAL); 6689779b714f4035642b87cbb7ef6cd8ac32848c930Chung-yih Wang return; 6699779b714f4035642b87cbb7ef6cd8ac32848c930Chung-yih Wang } 6709779b714f4035642b87cbb7ef6cd8ac32848c930Chung-yih Wang } 6719779b714f4035642b87cbb7ef6cd8ac32848c930Chung-yih Wang foregroundCall.switchWith(ringingCall); 6729779b714f4035642b87cbb7ef6cd8ac32848c930Chung-yih Wang } 673ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang setState(newState); 674ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 675ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang mOwner.onConnectionStateChanged(SipConnection.this); 676aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan if (DEBUG) Log.v(LOG_TAG, "+***+ connection state changed: " 677ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang + mPeer.getUriString() + ": " + mState 678ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang + " on phone " + getPhone()); 679ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 680ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 681ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 682ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang @Override 683903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan protected void onError(DisconnectCause cause) { 684aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan if (DEBUG) Log.d(LOG_TAG, "SIP error: " + cause); 685903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan onCallEnded(cause); 686ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 687ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang }; 688ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 689f5201ab71ff4d104265ab126e86afc6b81da8011Hung-ying Tyan public SipConnection(SipCall owner, SipProfile callee, 690f5201ab71ff4d104265ab126e86afc6b81da8011Hung-ying Tyan String originalNumber) { 691f5201ab71ff4d104265ab126e86afc6b81da8011Hung-ying Tyan super(originalNumber); 692ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang mOwner = owner; 693ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang mPeer = callee; 694f5201ab71ff4d104265ab126e86afc6b81da8011Hung-ying Tyan mOriginalNumber = originalNumber; 695f5201ab71ff4d104265ab126e86afc6b81da8011Hung-ying Tyan } 696f5201ab71ff4d104265ab126e86afc6b81da8011Hung-ying Tyan 697f5201ab71ff4d104265ab126e86afc6b81da8011Hung-ying Tyan public SipConnection(SipCall owner, SipProfile callee) { 698f5201ab71ff4d104265ab126e86afc6b81da8011Hung-ying Tyan this(owner, callee, getUriString(callee)); 699ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 700ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 701538e58fc757b0d10672235bc17b1380854845139Hung-ying Tyan @Override 702538e58fc757b0d10672235bc17b1380854845139Hung-ying Tyan public String getCnapName() { 703538e58fc757b0d10672235bc17b1380854845139Hung-ying Tyan String displayName = mPeer.getDisplayName(); 704538e58fc757b0d10672235bc17b1380854845139Hung-ying Tyan return TextUtils.isEmpty(displayName) ? null 705538e58fc757b0d10672235bc17b1380854845139Hung-ying Tyan : displayName; 706538e58fc757b0d10672235bc17b1380854845139Hung-ying Tyan } 707538e58fc757b0d10672235bc17b1380854845139Hung-ying Tyan 708538e58fc757b0d10672235bc17b1380854845139Hung-ying Tyan @Override 709538e58fc757b0d10672235bc17b1380854845139Hung-ying Tyan public int getNumberPresentation() { 710538e58fc757b0d10672235bc17b1380854845139Hung-ying Tyan return Connection.PRESENTATION_ALLOWED; 711538e58fc757b0d10672235bc17b1380854845139Hung-ying Tyan } 712538e58fc757b0d10672235bc17b1380854845139Hung-ying Tyan 713ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang void initIncomingCall(SipAudioCall sipAudioCall, Call.State newState) { 714ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang setState(newState); 715ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang mSipAudioCall = sipAudioCall; 716ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang sipAudioCall.setListener(mAdapter); // call back to set state 717ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang mIncoming = true; 718ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 719ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 720ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang void acceptCall() throws CallStateException { 721ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang try { 722194bbcce9ba15634500f542b9ea017b2cf154b45Hung-ying Tyan mSipAudioCall.answerCall(TIMEOUT_ANSWER_CALL); 723ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } catch (SipException e) { 724ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang throw new CallStateException("acceptCall(): " + e); 725ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 726ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 727ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 728ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang void changeOwner(SipCall owner) { 729ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang mOwner = owner; 730ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 731ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 732ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang AudioGroup getAudioGroup() { 733ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (mSipAudioCall == null) return null; 734ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return mSipAudioCall.getAudioGroup(); 735ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 736ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 737ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang void dial() throws SipException { 738ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang setState(Call.State.DIALING); 73984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mSipAudioCall = mSipManager.makeAudioCall(mProfile, mPeer, null, 740194bbcce9ba15634500f542b9ea017b2cf154b45Hung-ying Tyan TIMEOUT_MAKE_CALL); 741ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang mSipAudioCall.setListener(mAdapter); 742ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 743ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 744ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang void hold() throws CallStateException { 7453294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan setState(Call.State.HOLDING); 746ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang try { 747194bbcce9ba15634500f542b9ea017b2cf154b45Hung-ying Tyan mSipAudioCall.holdCall(TIMEOUT_HOLD_CALL); 748ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } catch (SipException e) { 749ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang throw new CallStateException("hold(): " + e); 750ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 751ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 752ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 7533294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan void unhold(AudioGroup audioGroup) throws CallStateException { 7543294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan mSipAudioCall.setAudioGroup(audioGroup); 7553294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan setState(Call.State.ACTIVE); 756ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang try { 757194bbcce9ba15634500f542b9ea017b2cf154b45Hung-ying Tyan mSipAudioCall.continueCall(TIMEOUT_HOLD_CALL); 758ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } catch (SipException e) { 759ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang throw new CallStateException("unhold(): " + e); 760ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 761ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 762ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 7631d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan void setMute(boolean muted) { 7641d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan if ((mSipAudioCall != null) && (muted != mSipAudioCall.isMuted())) { 7651d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan mSipAudioCall.toggleMute(); 7661d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan } 7671d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan } 7681d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan 7691d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan boolean getMute() { 7701d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan return (mSipAudioCall == null) ? false 7711d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan : mSipAudioCall.isMuted(); 7721d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan } 7731d12ef09a8e6ebc6638f4ff2f561c50c950023cbHung-ying Tyan 774ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang @Override 775ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang protected void setState(Call.State state) { 776ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (state == mState) return; 777ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang super.setState(state); 778ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang mState = state; 779ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 780ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 781ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang @Override 782ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public Call.State getState() { 783ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return mState; 784ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 785ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 786ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang @Override 787ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public boolean isIncoming() { 788ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return mIncoming; 789ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 790ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 791ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang @Override 792ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public String getAddress() { 793f5201ab71ff4d104265ab126e86afc6b81da8011Hung-ying Tyan // Phone app uses this to query caller ID. Return the original dial 794f5201ab71ff4d104265ab126e86afc6b81da8011Hung-ying Tyan // number (which may be a PSTN number) instead of the peer's SIP 795f5201ab71ff4d104265ab126e86afc6b81da8011Hung-ying Tyan // URI. 796f5201ab71ff4d104265ab126e86afc6b81da8011Hung-ying Tyan return mOriginalNumber; 797ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 798ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 799ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang @Override 800ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public SipCall getCall() { 801ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return mOwner; 802ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 803ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 804ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang @Override 805ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang protected Phone getPhone() { 806ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return mOwner.getPhone(); 807ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 808ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 809ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang @Override 810ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void hangup() throws CallStateException { 8113294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan synchronized (SipPhone.class) { 812aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan if (DEBUG) Log.d(LOG_TAG, "hangup conn: " + mPeer.getUriString() 813aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan + ": " + mState + ": on phone " 814aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan + getPhone().getPhoneName()); 815421c34c162098efe870574844a7ee49812bbb929Hung-ying Tyan if (!mState.isAlive()) return; 8163294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan try { 817421c34c162098efe870574844a7ee49812bbb929Hung-ying Tyan SipAudioCall sipAudioCall = mSipAudioCall; 818421c34c162098efe870574844a7ee49812bbb929Hung-ying Tyan if (sipAudioCall != null) { 819421c34c162098efe870574844a7ee49812bbb929Hung-ying Tyan sipAudioCall.setListener(null); 820421c34c162098efe870574844a7ee49812bbb929Hung-ying Tyan sipAudioCall.endCall(); 8212b4f5cfd9be5ceffc4745a45736e067a475a4dffHung-ying Tyan } 8223294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan } catch (SipException e) { 8233294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan throw new CallStateException("hangup(): " + e); 824421c34c162098efe870574844a7ee49812bbb929Hung-ying Tyan } finally { 82517956e626b38ce53da61e78af2c973ed41c9e461Hung-ying Tyan mAdapter.onCallEnded(((mState == Call.State.INCOMING) 82617956e626b38ce53da61e78af2c973ed41c9e461Hung-ying Tyan || (mState == Call.State.WAITING)) 82717956e626b38ce53da61e78af2c973ed41c9e461Hung-ying Tyan ? DisconnectCause.INCOMING_REJECTED 82817956e626b38ce53da61e78af2c973ed41c9e461Hung-ying Tyan : DisconnectCause.LOCAL); 8293294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan } 830ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 831ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 832ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 833ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang @Override 834ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void separate() throws CallStateException { 8353294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan synchronized (SipPhone.class) { 8366037a056ea0dda27a286ddcb527b323b58a1c7c7Hung-ying Tyan SipCall call = (getPhone() == SipPhone.this) 8376037a056ea0dda27a286ddcb527b323b58a1c7c7Hung-ying Tyan ? (SipCall) SipPhone.this.getBackgroundCall() 8386037a056ea0dda27a286ddcb527b323b58a1c7c7Hung-ying Tyan : (SipCall) SipPhone.this.getForegroundCall(); 8393294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan if (call.getState() != Call.State.IDLE) { 8403294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan throw new CallStateException( 8413294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan "cannot put conn back to a call in non-idle state: " 8423294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan + call.getState()); 8433294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan } 844aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan if (DEBUG) Log.d(LOG_TAG, "separate conn: " 845aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan + mPeer.getUriString() + " from " + mOwner + " back to " 846aeba1bc0c333f145469fc17a10c0bbcebd6dc30bHung-ying Tyan + call); 8473294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan 8486037a056ea0dda27a286ddcb527b323b58a1c7c7Hung-ying Tyan // separate the AudioGroup and connection from the original call 8496037a056ea0dda27a286ddcb527b323b58a1c7c7Hung-ying Tyan Phone originalPhone = getPhone(); 8503294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan AudioGroup audioGroup = call.getAudioGroup(); // may be null 8513294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan call.add(this); 8523294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan mSipAudioCall.setAudioGroup(audioGroup); 8536037a056ea0dda27a286ddcb527b323b58a1c7c7Hung-ying Tyan 8546037a056ea0dda27a286ddcb527b323b58a1c7c7Hung-ying Tyan // put the original call to bg; and the separated call becomes 8556037a056ea0dda27a286ddcb527b323b58a1c7c7Hung-ying Tyan // fg if it was in bg 8566037a056ea0dda27a286ddcb527b323b58a1c7c7Hung-ying Tyan originalPhone.switchHoldingAndActive(); 8576037a056ea0dda27a286ddcb527b323b58a1c7c7Hung-ying Tyan 8586037a056ea0dda27a286ddcb527b323b58a1c7c7Hung-ying Tyan // start audio and notify the phone app of the state change 8596037a056ea0dda27a286ddcb527b323b58a1c7c7Hung-ying Tyan call = (SipCall) SipPhone.this.getForegroundCall(); 8606037a056ea0dda27a286ddcb527b323b58a1c7c7Hung-ying Tyan mSipAudioCall.startAudio(); 8616037a056ea0dda27a286ddcb527b323b58a1c7c7Hung-ying Tyan call.onConnectionStateChanged(this); 862ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 863ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 864ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 865ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 866ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 867ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang private static Call.State getCallStateFrom(SipAudioCall sipAudioCall) { 868ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang if (sipAudioCall.isOnHold()) return Call.State.HOLDING; 86997963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan int sessionState = sipAudioCall.getState(); 870ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang switch (sessionState) { 87184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.READY_TO_CALL: return Call.State.IDLE; 87284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.INCOMING_CALL: 87384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.INCOMING_CALL_ANSWERING: return Call.State.INCOMING; 87484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.OUTGOING_CALL: return Call.State.DIALING; 87584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.OUTGOING_CALL_RING_BACK: return Call.State.ALERTING; 87684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.OUTGOING_CALL_CANCELING: return Call.State.DISCONNECTING; 87784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.IN_CALL: return Call.State.ACTIVE; 878ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang default: 879ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang Log.w(LOG_TAG, "illegal connection state: " + sessionState); 880ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang return Call.State.DISCONNECTED; 881ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 882ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 883ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 88484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private abstract class SipAudioCallAdapter extends SipAudioCall.Listener { 885ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang protected abstract void onCallEnded(Connection.DisconnectCause cause); 886903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan protected abstract void onError(Connection.DisconnectCause cause); 887ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 888ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang @Override 889ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void onCallEnded(SipAudioCall call) { 8908544560ccc43de7ff49d91866f461f5572f0b147Hung-ying Tyan onCallEnded(call.isInCall() 8918544560ccc43de7ff49d91866f461f5572f0b147Hung-ying Tyan ? Connection.DisconnectCause.NORMAL 8928544560ccc43de7ff49d91866f461f5572f0b147Hung-ying Tyan : Connection.DisconnectCause.INCOMING_MISSED); 893ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 894ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 895ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang @Override 896ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang public void onCallBusy(SipAudioCall call) { 897ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang onCallEnded(Connection.DisconnectCause.BUSY); 898ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 899ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang 900ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang @Override 90197963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan public void onError(SipAudioCall call, int errorCode, 902903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan String errorMessage) { 90313f6270eb14b409709c936b828e2a2fd40e427c4Hung-ying Tyan switch (errorCode) { 904c6548fd9eda7b58f5a2e2a9c01e3c7cafd42fafbHung-ying Tyan case SipErrorCode.SERVER_UNREACHABLE: 905c6548fd9eda7b58f5a2e2a9c01e3c7cafd42fafbHung-ying Tyan onError(Connection.DisconnectCause.SERVER_UNREACHABLE); 906c6548fd9eda7b58f5a2e2a9c01e3c7cafd42fafbHung-ying Tyan break; 90797963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan case SipErrorCode.PEER_NOT_REACHABLE: 908ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan onError(Connection.DisconnectCause.NUMBER_UNREACHABLE); 909ae076d3981fda732d54b6c6e37e5659b2e7ba130Hung-ying Tyan break; 91097963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan case SipErrorCode.INVALID_REMOTE_URI: 911903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan onError(Connection.DisconnectCause.INVALID_NUMBER); 912903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan break; 91397963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan case SipErrorCode.TIME_OUT: 91497963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan case SipErrorCode.TRANSACTION_TERMINTED: 9153d7606aa607b24817e37c264f2141ed7b2d50be0Hung-ying Tyan onError(Connection.DisconnectCause.TIMED_OUT); 916903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan break; 91797963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan case SipErrorCode.DATA_CONNECTION_LOST: 918d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan onError(Connection.DisconnectCause.LOST_SIGNAL); 919d231aa880ab006d51ffe03454c1fc082f1c97bb8Hung-ying Tyan break; 92097963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan case SipErrorCode.INVALID_CREDENTIALS: 921903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan onError(Connection.DisconnectCause.INVALID_CREDENTIALS); 922903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan break; 92300a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan case SipErrorCode.CROSS_DOMAIN_AUTHENTICATION: 92400a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan onError(Connection.DisconnectCause.OUT_OF_NETWORK); 92500a22064efef4f574e439079aae2deae1a087a31Hung-ying Tyan break; 92697963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan case SipErrorCode.SERVER_ERROR: 927624d5b4e8c20516516d0bff74479b9f5abdfe61cHung-ying Tyan onError(Connection.DisconnectCause.SERVER_ERROR); 928624d5b4e8c20516516d0bff74479b9f5abdfe61cHung-ying Tyan break; 929624d5b4e8c20516516d0bff74479b9f5abdfe61cHung-ying Tyan case SipErrorCode.SOCKET_ERROR: 93097963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan case SipErrorCode.CLIENT_ERROR: 931903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan default: 93297963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan Log.w(LOG_TAG, "error: " + SipErrorCode.toString(errorCode) 93397963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan + ": " + errorMessage); 934903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan onError(Connection.DisconnectCause.ERROR_UNSPECIFIED); 935903e1031605d715e904811b0dd06cc6a518f0048Hung-ying Tyan } 936ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 937ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang } 938ccd0b6953f5f77d1da5f540a3ba5ea71116e14f0Chung-yih Wang} 939