1363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang/* 2363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Copyright (C) 2010 The Android Open Source Project 3363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 4363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Licensed under the Apache License, Version 2.0 (the "License"); 5363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * you may not use this file except in compliance with the License. 6363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * You may obtain a copy of the License at 7363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 8363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * http://www.apache.org/licenses/LICENSE-2.0 9363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 10363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Unless required by applicable law or agreed to in writing, software 11363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * distributed under the License is distributed on an "AS IS" BASIS, 12363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * See the License for the specific language governing permissions and 14363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * limitations under the License. 15363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 16363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 17363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wangpackage android.net.sip; 18363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 1984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport android.content.Context; 2084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport android.media.AudioManager; 2184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport android.net.rtp.AudioCodec; 22363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wangimport android.net.rtp.AudioGroup; 23363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wangimport android.net.rtp.AudioStream; 2484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport android.net.rtp.RtpStream; 2584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport android.net.sip.SimpleSessionDescription.Media; 2684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport android.net.wifi.WifiManager; 27363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wangimport android.os.Message; 2884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport android.os.RemoteException; 292093561a58e602450f6e4f2aae4831edd1b840f4repo syncimport android.text.TextUtils; 3084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport android.util.Log; 3184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 3284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport java.io.IOException; 3384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport java.net.InetAddress; 3484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport java.net.UnknownHostException; 3584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport java.util.ArrayList; 3684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport java.util.HashMap; 3784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport java.util.List; 3884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyanimport java.util.Map; 39363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 40363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang/** 4102b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * Handles an Internet audio call over SIP. You can instantiate this class with {@link SipManager}, 4202b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * using {@link SipManager#makeAudioCall makeAudioCall()} and {@link SipManager#takeAudioCall 4302b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * takeAudioCall()}. 44e87b644402642bad7147f915849bfa0eadaea446Hung-ying Tyan * 4502b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * <p class="note"><strong>Note:</strong> Using this class require the 46e87b644402642bad7147f915849bfa0eadaea446Hung-ying Tyan * {@link android.Manifest.permission#INTERNET} and 473aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * {@link android.Manifest.permission#USE_SIP} permissions. In addition, {@link 4802b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * #startAudio} requires the 49e87b644402642bad7147f915849bfa0eadaea446Hung-ying Tyan * {@link android.Manifest.permission#RECORD_AUDIO}, 5002b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * {@link android.Manifest.permission#ACCESS_WIFI_STATE}, and 5102b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * {@link android.Manifest.permission#WAKE_LOCK} permissions; and {@link #setSpeakerMode 5202b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * setSpeakerMode()} requires the 5302b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS} permission.</p> 543aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * 553aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * <div class="special reference"> 563aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * <h3>Developer Guides</h3> 573aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * <p>For more information about using SIP, read the 583aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * <a href="{@docRoot}guide/topics/network/sip.html">Session Initiation Protocol</a> 593aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * developer guide.</p> 603aef8e1d1b2f0b87d470bcccf37ba4ebb6560c45Joe Fernandez * </div> 61363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 623a4197e642e9c70f1fe00c2cba30f0f957d36bfcHung-ying Tyanpublic class SipAudioCall { 6384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private static final String TAG = SipAudioCall.class.getSimpleName(); 6484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private static final boolean RELEASE_SOCKET = true; 6584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private static final boolean DONT_RELEASE_SOCKET = false; 6684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private static final int SESSION_TIMEOUT = 5; // in seconds 67307f15faafa5a38d9b3b314df22778cd11685d7brepo sync private static final int TRANSFER_TIMEOUT = 15; // in seconds 6884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 6902b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main /** Listener for events relating to a SIP call, such as when a call is being 7002b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * recieved ("on ringing") or a call is outgoing ("on calling"). 7102b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * <p>Many of these events are also received by {@link SipSession.Listener}.</p> 7202b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main */ 7384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public static class Listener { 74363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 75363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Called when the call object is ready to make another call. 7608faac3c26e12863858e1534985dd950193f755fHung-ying Tyan * The default implementation calls {@link #onChanged}. 77363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 78363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @param call the call object that is ready to make another call 79363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 8084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onReadyToCall(SipAudioCall call) { 8184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan onChanged(call); 8284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 83363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 84363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 85363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Called when a request is sent out to initiate a new call. 8608faac3c26e12863858e1534985dd950193f755fHung-ying Tyan * The default implementation calls {@link #onChanged}. 87363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 88363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @param call the call object that carries out the audio call 89363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 9084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onCalling(SipAudioCall call) { 9184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan onChanged(call); 9284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 93363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 94363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 95363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Called when a new call comes in. 9608faac3c26e12863858e1534985dd950193f755fHung-ying Tyan * The default implementation calls {@link #onChanged}. 97363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 98363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @param call the call object that carries out the audio call 99363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @param caller the SIP profile of the caller 100363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 10184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onRinging(SipAudioCall call, SipProfile caller) { 10284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan onChanged(call); 10384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 104363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 105363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 10684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * Called when a RINGING response is received for the INVITE request 10708faac3c26e12863858e1534985dd950193f755fHung-ying Tyan * sent. The default implementation calls {@link #onChanged}. 108363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 109363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @param call the call object that carries out the audio call 110363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 11184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onRingingBack(SipAudioCall call) { 11284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan onChanged(call); 11384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 114363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 115363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 116363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Called when the session is established. 11708faac3c26e12863858e1534985dd950193f755fHung-ying Tyan * The default implementation calls {@link #onChanged}. 118363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 119363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @param call the call object that carries out the audio call 120363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 12184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onCallEstablished(SipAudioCall call) { 12284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan onChanged(call); 12384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 124363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 125363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 126363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Called when the session is terminated. 12708faac3c26e12863858e1534985dd950193f755fHung-ying Tyan * The default implementation calls {@link #onChanged}. 128363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 129363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @param call the call object that carries out the audio call 130363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 13184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onCallEnded(SipAudioCall call) { 13284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan onChanged(call); 13384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 134363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 135363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 136363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Called when the peer is busy during session initialization. 13708faac3c26e12863858e1534985dd950193f755fHung-ying Tyan * The default implementation calls {@link #onChanged}. 138363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 139363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @param call the call object that carries out the audio call 140363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 14184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onCallBusy(SipAudioCall call) { 14284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan onChanged(call); 14384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 144363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 145363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 146363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Called when the call is on hold. 14708faac3c26e12863858e1534985dd950193f755fHung-ying Tyan * The default implementation calls {@link #onChanged}. 148363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 149363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @param call the call object that carries out the audio call 150363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 15184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onCallHeld(SipAudioCall call) { 15284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan onChanged(call); 15384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 154363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 155363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 15684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * Called when an error occurs. The default implementation is no op. 157363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 158363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @param call the call object that carries out the audio call 15913f6270eb14b409709c936b828e2a2fd40e427c4Hung-ying Tyan * @param errorCode error code of this error 160363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @param errorMessage error message 16197963794af1e18674dd111e3ad344d90b16c922cHung-ying Tyan * @see SipErrorCode 162363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 16384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onError(SipAudioCall call, int errorCode, 16484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan String errorMessage) { 16584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan // no-op 16684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 16784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 16884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan /** 16984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * Called when an event occurs and the corresponding callback is not 17084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * overridden. The default implementation is no op. Error events are 17184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * not re-directed to this callback and are handled in {@link #onError}. 17284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan */ 17384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onChanged(SipAudioCall call) { 17484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan // no-op 17584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 176363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang } 177363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 17884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private Context mContext; 17984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private SipProfile mLocalProfile; 18084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private SipAudioCall.Listener mListener; 18184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private SipSession mSipSession; 1821aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync private SipSession mTransferringSession; 18384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 18484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private long mSessionId = System.currentTimeMillis(); 18584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private String mPeerSd; 18684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 18784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private AudioStream mAudioStream; 18884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private AudioGroup mAudioGroup; 18984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 19084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private boolean mInCall = false; 19184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private boolean mMuted = false; 19284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private boolean mHold = false; 19384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 19484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private SipProfile mPendingCallRequest; 19584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private WifiManager mWm; 19684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private WifiManager.WifiLock mWifiHighPerfLock; 19784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 19884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private int mErrorCode = SipErrorCode.NO_ERROR; 19984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private String mErrorMessage; 20084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 201363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 20284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * Creates a call object with the local SIP profile. 20384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @param context the context for accessing system services such as 20484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * ringtone, audio, WIFI etc 205363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 20684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public SipAudioCall(Context context, SipProfile localProfile) { 20784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mContext = context; 20884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mLocalProfile = localProfile; 20984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mWm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 210363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang } 211363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 212363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 213363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Sets the listener to listen to the audio call events. The method calls 21402b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * {@link #setListener setListener(listener, false)}. 215363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 216363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @param listener to listen to the audio call events of this object 217363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @see #setListener(Listener, boolean) 218363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 21984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void setListener(SipAudioCall.Listener listener) { 22084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan setListener(listener, false); 22184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 222363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 223363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 224363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Sets the listener to listen to the audio call events. A 225363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * {@link SipAudioCall} can only hold one listener at a time. Subsequent 226363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * calls to this method override the previous listener. 227363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 228363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @param listener to listen to the audio call events of this object 229363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @param callbackImmediately set to true if the caller wants to be called 230363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * back immediately on the current state 231363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 23284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void setListener(SipAudioCall.Listener listener, 23384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan boolean callbackImmediately) { 23484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mListener = listener; 23584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan try { 23684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if ((listener == null) || !callbackImmediately) { 23784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan // do nothing 23884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } else if (mErrorCode != SipErrorCode.NO_ERROR) { 23984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan listener.onError(this, mErrorCode, mErrorMessage); 24084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } else if (mInCall) { 24184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (mHold) { 24284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan listener.onCallHeld(this); 24384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } else { 24484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan listener.onCallEstablished(this); 24584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 24684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } else { 24784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan int state = getState(); 24884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan switch (state) { 24984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.READY_TO_CALL: 25084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan listener.onReadyToCall(this); 25184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan break; 25284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.INCOMING_CALL: 25384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan listener.onRinging(this, getPeerProfile()); 25484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan break; 25584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.OUTGOING_CALL: 25684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan listener.onCalling(this); 25784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan break; 25884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan case SipSession.State.OUTGOING_CALL_RING_BACK: 25984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan listener.onRingingBack(this); 26084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan break; 26184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 26284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 26384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } catch (Throwable t) { 26484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Log.e(TAG, "setListener()", t); 26584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 26684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 26784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 26884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan /** 26984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * Checks if the call is established. 27084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * 27184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @return true if the call is established 27284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan */ 27308faac3c26e12863858e1534985dd950193f755fHung-ying Tyan public boolean isInCall() { 27408faac3c26e12863858e1534985dd950193f755fHung-ying Tyan synchronized (this) { 27508faac3c26e12863858e1534985dd950193f755fHung-ying Tyan return mInCall; 27608faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } 27784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 27884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 27984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan /** 28084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * Checks if the call is on hold. 28184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * 28284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @return true if the call is on hold 28384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan */ 28408faac3c26e12863858e1534985dd950193f755fHung-ying Tyan public boolean isOnHold() { 28508faac3c26e12863858e1534985dd950193f755fHung-ying Tyan synchronized (this) { 28608faac3c26e12863858e1534985dd950193f755fHung-ying Tyan return mHold; 28708faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } 28884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 289363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 290363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 291afa583e6557557577188c3e40146ac8d6f2aa7c7Hung-ying Tyan * Closes this object. This object is not usable after being closed. 292363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 29384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void close() { 29484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan close(true); 29584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 29684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 29784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private synchronized void close(boolean closeRtp) { 29884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (closeRtp) stopCall(RELEASE_SOCKET); 29984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 30084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mInCall = false; 30184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mHold = false; 30284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mSessionId = System.currentTimeMillis(); 30384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mErrorCode = SipErrorCode.NO_ERROR; 30484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mErrorMessage = null; 30584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 30684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (mSipSession != null) { 30784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mSipSession.setListener(null); 30884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mSipSession = null; 30984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 31084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 311363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 312363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 31384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * Gets the local SIP profile. 314363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 31584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @return the local SIP profile 316363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 31708faac3c26e12863858e1534985dd950193f755fHung-ying Tyan public SipProfile getLocalProfile() { 31808faac3c26e12863858e1534985dd950193f755fHung-ying Tyan synchronized (this) { 31908faac3c26e12863858e1534985dd950193f755fHung-ying Tyan return mLocalProfile; 32008faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } 32184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 322363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 323363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 32484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * Gets the peer's SIP profile. 32584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * 32684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @return the peer's SIP profile 327afa583e6557557577188c3e40146ac8d6f2aa7c7Hung-ying Tyan */ 32808faac3c26e12863858e1534985dd950193f755fHung-ying Tyan public SipProfile getPeerProfile() { 32908faac3c26e12863858e1534985dd950193f755fHung-ying Tyan synchronized (this) { 33008faac3c26e12863858e1534985dd950193f755fHung-ying Tyan return (mSipSession == null) ? null : mSipSession.getPeerProfile(); 33108faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } 33284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 33384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 33484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan /** 33584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * Gets the state of the {@link SipSession} that carries this call. 33684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * The value returned must be one of the states in {@link SipSession.State}. 33784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * 33884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @return the session state 33984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan */ 34008faac3c26e12863858e1534985dd950193f755fHung-ying Tyan public int getState() { 34108faac3c26e12863858e1534985dd950193f755fHung-ying Tyan synchronized (this) { 34208faac3c26e12863858e1534985dd950193f755fHung-ying Tyan if (mSipSession == null) return SipSession.State.READY_TO_CALL; 34308faac3c26e12863858e1534985dd950193f755fHung-ying Tyan return mSipSession.getState(); 34408faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } 34584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 34684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 34784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 34884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan /** 34984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * Gets the {@link SipSession} that carries this call. 35084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * 35184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @return the session object that carries this call 35284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @hide 35384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan */ 35408faac3c26e12863858e1534985dd950193f755fHung-ying Tyan public SipSession getSipSession() { 35508faac3c26e12863858e1534985dd950193f755fHung-ying Tyan synchronized (this) { 35608faac3c26e12863858e1534985dd950193f755fHung-ying Tyan return mSipSession; 35708faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } 35884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 35984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 3601aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync private synchronized void transferToNewSession() { 3611aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if (mTransferringSession == null) return; 3621aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync SipSession origin = mSipSession; 3631aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync mSipSession = mTransferringSession; 3641aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync mTransferringSession = null; 3651aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync 3661aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync // stop the replaced call. 3671aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if (mAudioStream != null) { 3681aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync mAudioStream.join(null); 3691aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } else { 3701aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync try { 3711aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync mAudioStream = new AudioStream(InetAddress.getByName( 3721aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync getLocalIp())); 3731aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } catch (Throwable t) { 3741aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync Log.i(TAG, "transferToNewSession(): " + t); 3751aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 3761aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 3771aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if (origin != null) origin.endCall(); 3781aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync startAudio(); 3791aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 3801aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync 38184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private SipSession.Listener createListener() { 38284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan return new SipSession.Listener() { 38384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan @Override 38484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onCalling(SipSession session) { 38584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Log.d(TAG, "calling... " + session); 38684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Listener listener = mListener; 38784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (listener != null) { 38884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan try { 38984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan listener.onCalling(SipAudioCall.this); 39084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } catch (Throwable t) { 39184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Log.i(TAG, "onCalling(): " + t); 39284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 39384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 39484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 39584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 39684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan @Override 39784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onRingingBack(SipSession session) { 39884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Log.d(TAG, "sip call ringing back: " + session); 39984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Listener listener = mListener; 40084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (listener != null) { 40184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan try { 40284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan listener.onRingingBack(SipAudioCall.this); 40384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } catch (Throwable t) { 40484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Log.i(TAG, "onRingingBack(): " + t); 40584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 40684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 40784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 40884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 40984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan @Override 41008faac3c26e12863858e1534985dd950193f755fHung-ying Tyan public void onRinging(SipSession session, 41184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan SipProfile peerProfile, String sessionDescription) { 4122093561a58e602450f6e4f2aae4831edd1b840f4repo sync // this callback is triggered only for reinvite. 41308faac3c26e12863858e1534985dd950193f755fHung-ying Tyan synchronized (SipAudioCall.this) { 41408faac3c26e12863858e1534985dd950193f755fHung-ying Tyan if ((mSipSession == null) || !mInCall 41508faac3c26e12863858e1534985dd950193f755fHung-ying Tyan || !session.getCallId().equals( 41608faac3c26e12863858e1534985dd950193f755fHung-ying Tyan mSipSession.getCallId())) { 41708faac3c26e12863858e1534985dd950193f755fHung-ying Tyan // should not happen 41808faac3c26e12863858e1534985dd950193f755fHung-ying Tyan session.endCall(); 41908faac3c26e12863858e1534985dd950193f755fHung-ying Tyan return; 42008faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } 42184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 42208faac3c26e12863858e1534985dd950193f755fHung-ying Tyan // session changing request 42308faac3c26e12863858e1534985dd950193f755fHung-ying Tyan try { 42408faac3c26e12863858e1534985dd950193f755fHung-ying Tyan String answer = createAnswer(sessionDescription).encode(); 42508faac3c26e12863858e1534985dd950193f755fHung-ying Tyan mSipSession.answerCall(answer, SESSION_TIMEOUT); 42608faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } catch (Throwable e) { 42708faac3c26e12863858e1534985dd950193f755fHung-ying Tyan Log.e(TAG, "onRinging()", e); 42808faac3c26e12863858e1534985dd950193f755fHung-ying Tyan session.endCall(); 42908faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } 43084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 43184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 43284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 43384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan @Override 43484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onCallEstablished(SipSession session, 43584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan String sessionDescription) { 43684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mPeerSd = sessionDescription; 43784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Log.v(TAG, "onCallEstablished()" + mPeerSd); 43884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 4391aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync // TODO: how to notify the UI that the remote party is changed 4401aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if ((mTransferringSession != null) 4411aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync && (session == mTransferringSession)) { 4421aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync transferToNewSession(); 4431aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync return; 4441aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 4451aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync 44684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Listener listener = mListener; 44784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (listener != null) { 44884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan try { 44984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (mHold) { 45084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan listener.onCallHeld(SipAudioCall.this); 45184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } else { 45284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan listener.onCallEstablished(SipAudioCall.this); 45384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 45484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } catch (Throwable t) { 45584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Log.i(TAG, "onCallEstablished(): " + t); 45684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 45784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 45884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 45984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 46084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan @Override 46184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onCallEnded(SipSession session) { 4621aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync Log.d(TAG, "sip call ended: " + session + " mSipSession:" + mSipSession); 4631aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync // reset the trasnferring session if it is the one. 4641aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if (session == mTransferringSession) { 4651aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync mTransferringSession = null; 4661aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync return; 4671aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 4681aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync // or ignore the event if the original session is being 4691aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync // transferred to the new one. 4701aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync if ((mTransferringSession != null) || 4711aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync (session != mSipSession)) return; 4721aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync 47384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Listener listener = mListener; 47484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (listener != null) { 47584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan try { 47684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan listener.onCallEnded(SipAudioCall.this); 47784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } catch (Throwable t) { 47884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Log.i(TAG, "onCallEnded(): " + t); 47984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 48084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 48184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan close(); 48284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 48384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 48484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan @Override 48584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onCallBusy(SipSession session) { 48684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Log.d(TAG, "sip call busy: " + session); 48784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Listener listener = mListener; 48884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (listener != null) { 48984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan try { 49084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan listener.onCallBusy(SipAudioCall.this); 49184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } catch (Throwable t) { 49284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Log.i(TAG, "onCallBusy(): " + t); 49384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 49484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 49584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan close(false); 49684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 49784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 49884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan @Override 49984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onCallChangeFailed(SipSession session, int errorCode, 50084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan String message) { 50184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Log.d(TAG, "sip call change failed: " + message); 50284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mErrorCode = errorCode; 50384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mErrorMessage = message; 50484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Listener listener = mListener; 50584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (listener != null) { 50684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan try { 50784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan listener.onError(SipAudioCall.this, mErrorCode, 50884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan message); 50984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } catch (Throwable t) { 51084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Log.i(TAG, "onCallBusy(): " + t); 51184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 51284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 51384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 51484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 51584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan @Override 51684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onError(SipSession session, int errorCode, 51784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan String message) { 51884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan SipAudioCall.this.onError(errorCode, message); 51984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 52084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 52184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan @Override 52284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onRegistering(SipSession session) { 52384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan // irrelevant 52484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 52584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 52684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan @Override 52784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onRegistrationTimeout(SipSession session) { 52884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan // irrelevant 52984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 53084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 53184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan @Override 53284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onRegistrationFailed(SipSession session, int errorCode, 53384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan String message) { 53484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan // irrelevant 53584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 53684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 53784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan @Override 53884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void onRegistrationDone(SipSession session, int duration) { 53984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan // irrelevant 54084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 5411aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync 5421aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync @Override 5431aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync public void onCallTransferring(SipSession newSession, 5441aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync String sessionDescription) { 5451aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync Log.v(TAG, "onCallTransferring mSipSession:" 5461aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync + mSipSession + " newSession:" + newSession); 5471aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync mTransferringSession = newSession; 5481aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync try { 549307f15faafa5a38d9b3b314df22778cd11685d7brepo sync if (sessionDescription == null) { 550307f15faafa5a38d9b3b314df22778cd11685d7brepo sync newSession.makeCall(newSession.getPeerProfile(), 551307f15faafa5a38d9b3b314df22778cd11685d7brepo sync createOffer().encode(), TRANSFER_TIMEOUT); 552307f15faafa5a38d9b3b314df22778cd11685d7brepo sync } else { 553307f15faafa5a38d9b3b314df22778cd11685d7brepo sync String answer = createAnswer(sessionDescription).encode(); 554307f15faafa5a38d9b3b314df22778cd11685d7brepo sync newSession.answerCall(answer, SESSION_TIMEOUT); 555307f15faafa5a38d9b3b314df22778cd11685d7brepo sync } 5561aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } catch (Throwable e) { 5571aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync Log.e(TAG, "onCallTransferring()", e); 5581aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync newSession.endCall(); 5591aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 5601aceda35cc607856ec2e960e0c6cfc6aea87ab8erepo sync } 56184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan }; 56284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 56384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 56484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private void onError(int errorCode, String message) { 56584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Log.d(TAG, "sip session error: " 56684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan + SipErrorCode.toString(errorCode) + ": " + message); 56784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mErrorCode = errorCode; 56884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mErrorMessage = message; 56984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Listener listener = mListener; 57084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (listener != null) { 57184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan try { 57284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan listener.onError(this, errorCode, message); 57384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } catch (Throwable t) { 57484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Log.i(TAG, "onError(): " + t); 57584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 57684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 57784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan synchronized (this) { 57884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if ((errorCode == SipErrorCode.DATA_CONNECTION_LOST) 57984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan || !isInCall()) { 58084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan close(true); 58184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 58284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 58384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 584afa583e6557557577188c3e40146ac8d6f2aa7c7Hung-ying Tyan 585afa583e6557557577188c3e40146ac8d6f2aa7c7Hung-ying Tyan /** 586363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Attaches an incoming call to this call object. 587363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 588363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @param session the session that receives the incoming call 58995b15c35608fe3ea679c8a478c6cbd841623371eChia-chi Yeh * @param sessionDescription the session description of the incoming call 59084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @throws SipException if the SIP service fails to attach this object to 5915bd3782f244212cd8ef51bf9f3578869b08b4e18Hung-ying Tyan * the session or VOIP API is not supported by the device 5925bd3782f244212cd8ef51bf9f3578869b08b4e18Hung-ying Tyan * @see SipManager#isVoipSupported 59384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan */ 59408faac3c26e12863858e1534985dd950193f755fHung-ying Tyan public void attachCall(SipSession session, String sessionDescription) 59508faac3c26e12863858e1534985dd950193f755fHung-ying Tyan throws SipException { 5965bd3782f244212cd8ef51bf9f3578869b08b4e18Hung-ying Tyan if (!SipManager.isVoipSupported(mContext)) { 5975bd3782f244212cd8ef51bf9f3578869b08b4e18Hung-ying Tyan throw new SipException("VOIP API is not supported"); 5985bd3782f244212cd8ef51bf9f3578869b08b4e18Hung-ying Tyan } 5995bd3782f244212cd8ef51bf9f3578869b08b4e18Hung-ying Tyan 60008faac3c26e12863858e1534985dd950193f755fHung-ying Tyan synchronized (this) { 60108faac3c26e12863858e1534985dd950193f755fHung-ying Tyan mSipSession = session; 60208faac3c26e12863858e1534985dd950193f755fHung-ying Tyan mPeerSd = sessionDescription; 60308faac3c26e12863858e1534985dd950193f755fHung-ying Tyan Log.v(TAG, "attachCall()" + mPeerSd); 60408faac3c26e12863858e1534985dd950193f755fHung-ying Tyan try { 60508faac3c26e12863858e1534985dd950193f755fHung-ying Tyan session.setListener(createListener()); 60608faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } catch (Throwable e) { 60708faac3c26e12863858e1534985dd950193f755fHung-ying Tyan Log.e(TAG, "attachCall()", e); 60808faac3c26e12863858e1534985dd950193f755fHung-ying Tyan throwSipException(e); 60908faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } 61084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 61184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 61284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 61384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan /** 61484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * Initiates an audio call to the specified profile. The attempt will be 61584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * timed out if the call is not established within {@code timeout} seconds 61602b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * and {@link Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)} 61784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * will be called. 61884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * 61908faac3c26e12863858e1534985dd950193f755fHung-ying Tyan * @param peerProfile the SIP profile to make the call to 6203a4197e642e9c70f1fe00c2cba30f0f957d36bfcHung-ying Tyan * @param sipSession the {@link SipSession} for carrying out the call 62184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @param timeout the timeout value in seconds. Default value (defined by 62284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * SIP protocol) is used if {@code timeout} is zero or negative. 62302b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * @see Listener#onError 62484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @throws SipException if the SIP service fails to create a session for the 6255bd3782f244212cd8ef51bf9f3578869b08b4e18Hung-ying Tyan * call or VOIP API is not supported by the device 6265bd3782f244212cd8ef51bf9f3578869b08b4e18Hung-ying Tyan * @see SipManager#isVoipSupported 627363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 62808faac3c26e12863858e1534985dd950193f755fHung-ying Tyan public void makeCall(SipProfile peerProfile, SipSession sipSession, 62908faac3c26e12863858e1534985dd950193f755fHung-ying Tyan int timeout) throws SipException { 6305bd3782f244212cd8ef51bf9f3578869b08b4e18Hung-ying Tyan if (!SipManager.isVoipSupported(mContext)) { 6315bd3782f244212cd8ef51bf9f3578869b08b4e18Hung-ying Tyan throw new SipException("VOIP API is not supported"); 6325bd3782f244212cd8ef51bf9f3578869b08b4e18Hung-ying Tyan } 6335bd3782f244212cd8ef51bf9f3578869b08b4e18Hung-ying Tyan 63408faac3c26e12863858e1534985dd950193f755fHung-ying Tyan synchronized (this) { 63508faac3c26e12863858e1534985dd950193f755fHung-ying Tyan mSipSession = sipSession; 63608faac3c26e12863858e1534985dd950193f755fHung-ying Tyan try { 63708faac3c26e12863858e1534985dd950193f755fHung-ying Tyan mAudioStream = new AudioStream(InetAddress.getByName( 63808faac3c26e12863858e1534985dd950193f755fHung-ying Tyan getLocalIp())); 63908faac3c26e12863858e1534985dd950193f755fHung-ying Tyan sipSession.setListener(createListener()); 64008faac3c26e12863858e1534985dd950193f755fHung-ying Tyan sipSession.makeCall(peerProfile, createOffer().encode(), 64108faac3c26e12863858e1534985dd950193f755fHung-ying Tyan timeout); 64208faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } catch (IOException e) { 64308faac3c26e12863858e1534985dd950193f755fHung-ying Tyan throw new SipException("makeCall()", e); 64408faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } 64584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 64684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 647363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 64884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan /** 64984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * Ends a call. 65084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @throws SipException if the SIP service fails to end the call 65184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan */ 65208faac3c26e12863858e1534985dd950193f755fHung-ying Tyan public void endCall() throws SipException { 65308faac3c26e12863858e1534985dd950193f755fHung-ying Tyan synchronized (this) { 65408faac3c26e12863858e1534985dd950193f755fHung-ying Tyan stopCall(RELEASE_SOCKET); 65508faac3c26e12863858e1534985dd950193f755fHung-ying Tyan mInCall = false; 65684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 65708faac3c26e12863858e1534985dd950193f755fHung-ying Tyan // perform the above local ops first and then network op 65808faac3c26e12863858e1534985dd950193f755fHung-ying Tyan if (mSipSession != null) mSipSession.endCall(); 65908faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } 66084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 661363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 662363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 663286bb5a00bdb9f0cb0815aef441ec72f231c84eaHung-ying Tyan * Puts a call on hold. When succeeds, {@link Listener#onCallHeld} is 6649352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan * called. The attempt will be timed out if the call is not established 6659352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan * within {@code timeout} seconds and 66602b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * {@link Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)} 6679352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan * will be called. 6689352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan * 66984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @param timeout the timeout value in seconds. Default value (defined by 67084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * SIP protocol) is used if {@code timeout} is zero or negative. 67102b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * @see Listener#onError 67284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @throws SipException if the SIP service fails to hold the call 673363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 67408faac3c26e12863858e1534985dd950193f755fHung-ying Tyan public void holdCall(int timeout) throws SipException { 67508faac3c26e12863858e1534985dd950193f755fHung-ying Tyan synchronized (this) { 676fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan if (mHold) return; 6775bd3782f244212cd8ef51bf9f3578869b08b4e18Hung-ying Tyan if (mSipSession == null) { 6785bd3782f244212cd8ef51bf9f3578869b08b4e18Hung-ying Tyan throw new SipException("Not in a call to hold call"); 6795bd3782f244212cd8ef51bf9f3578869b08b4e18Hung-ying Tyan } 68008faac3c26e12863858e1534985dd950193f755fHung-ying Tyan mSipSession.changeCall(createHoldOffer().encode(), timeout); 68108faac3c26e12863858e1534985dd950193f755fHung-ying Tyan mHold = true; 682fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan setAudioGroupMode(); 68308faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } 68484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 685363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 6869352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan /** 6879352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan * Answers a call. The attempt will be timed out if the call is not 6889352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan * established within {@code timeout} seconds and 68902b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * {@link Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)} 6909352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan * will be called. 6919352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan * 69284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @param timeout the timeout value in seconds. Default value (defined by 69384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * SIP protocol) is used if {@code timeout} is zero or negative. 69402b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * @see Listener#onError 69584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @throws SipException if the SIP service fails to answer the call 6969352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan */ 69708faac3c26e12863858e1534985dd950193f755fHung-ying Tyan public void answerCall(int timeout) throws SipException { 69808faac3c26e12863858e1534985dd950193f755fHung-ying Tyan synchronized (this) { 6995bd3782f244212cd8ef51bf9f3578869b08b4e18Hung-ying Tyan if (mSipSession == null) { 7005bd3782f244212cd8ef51bf9f3578869b08b4e18Hung-ying Tyan throw new SipException("No call to answer"); 7015bd3782f244212cd8ef51bf9f3578869b08b4e18Hung-ying Tyan } 70208faac3c26e12863858e1534985dd950193f755fHung-ying Tyan try { 70308faac3c26e12863858e1534985dd950193f755fHung-ying Tyan mAudioStream = new AudioStream(InetAddress.getByName( 70408faac3c26e12863858e1534985dd950193f755fHung-ying Tyan getLocalIp())); 70508faac3c26e12863858e1534985dd950193f755fHung-ying Tyan mSipSession.answerCall(createAnswer(mPeerSd).encode(), timeout); 70608faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } catch (IOException e) { 70708faac3c26e12863858e1534985dd950193f755fHung-ying Tyan throw new SipException("answerCall()", e); 70808faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } 70984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 71084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 711363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 712363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 713363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Continues a call that's on hold. When succeeds, 7149352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan * {@link Listener#onCallEstablished} is called. The attempt will be timed 7159352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan * out if the call is not established within {@code timeout} seconds and 71602b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * {@link Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)} 7179352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan * will be called. 7189352cf1a4d46492fc48a20f7d825a9bcb6e8b365Hung-ying Tyan * 71984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @param timeout the timeout value in seconds. Default value (defined by 72084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * SIP protocol) is used if {@code timeout} is zero or negative. 72102b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * @see Listener#onError 72284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @throws SipException if the SIP service fails to unhold the call 723363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 72408faac3c26e12863858e1534985dd950193f755fHung-ying Tyan public void continueCall(int timeout) throws SipException { 72508faac3c26e12863858e1534985dd950193f755fHung-ying Tyan synchronized (this) { 72608faac3c26e12863858e1534985dd950193f755fHung-ying Tyan if (!mHold) return; 72708faac3c26e12863858e1534985dd950193f755fHung-ying Tyan mSipSession.changeCall(createContinueOffer().encode(), timeout); 72808faac3c26e12863858e1534985dd950193f755fHung-ying Tyan mHold = false; 729fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan setAudioGroupMode(); 73008faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } 73184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 732363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 73384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private SimpleSessionDescription createOffer() { 73484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan SimpleSessionDescription offer = 73584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan new SimpleSessionDescription(mSessionId, getLocalIp()); 73684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan AudioCodec[] codecs = AudioCodec.getCodecs(); 73784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Media media = offer.newMedia( 73884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP"); 73984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan for (AudioCodec codec : AudioCodec.getCodecs()) { 74084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan media.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp); 74184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 74284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan media.setRtpPayload(127, "telephone-event/8000", "0-15"); 74384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan return offer; 74484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 745363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 74684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private SimpleSessionDescription createAnswer(String offerSd) { 7472093561a58e602450f6e4f2aae4831edd1b840f4repo sync if (TextUtils.isEmpty(offerSd)) return createOffer(); 74884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan SimpleSessionDescription offer = 74984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan new SimpleSessionDescription(offerSd); 75084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan SimpleSessionDescription answer = 75184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan new SimpleSessionDescription(mSessionId, getLocalIp()); 75284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan AudioCodec codec = null; 75384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan for (Media media : offer.getMedia()) { 75484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if ((codec == null) && (media.getPort() > 0) 75584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan && "audio".equals(media.getType()) 75684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan && "RTP/AVP".equals(media.getProtocol())) { 75784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan // Find the first audio codec we supported. 75884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan for (int type : media.getRtpPayloadTypes()) { 75984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan codec = AudioCodec.getCodec(type, media.getRtpmap(type), 76084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan media.getFmtp(type)); 76184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (codec != null) { 76284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan break; 76384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 76484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 76584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (codec != null) { 76684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Media reply = answer.newMedia( 76784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP"); 76884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan reply.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp); 769363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 77084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan // Check if DTMF is supported in the same media. 77184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan for (int type : media.getRtpPayloadTypes()) { 77284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan String rtpmap = media.getRtpmap(type); 77384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if ((type != codec.type) && (rtpmap != null) 77484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan && rtpmap.startsWith("telephone-event")) { 77584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan reply.setRtpPayload( 77684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan type, rtpmap, media.getFmtp(type)); 77784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 77884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 77984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 78084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan // Handle recvonly and sendonly. 78184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (media.getAttribute("recvonly") != null) { 78284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan answer.setAttribute("sendonly", ""); 78384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } else if(media.getAttribute("sendonly") != null) { 78484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan answer.setAttribute("recvonly", ""); 78584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } else if(offer.getAttribute("recvonly") != null) { 78684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan answer.setAttribute("sendonly", ""); 78784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } else if(offer.getAttribute("sendonly") != null) { 78884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan answer.setAttribute("recvonly", ""); 78984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 79084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan continue; 79184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 79284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 79384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan // Reject the media. 79484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Media reply = answer.newMedia( 79584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan media.getType(), 0, 1, media.getProtocol()); 79684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan for (String format : media.getFormats()) { 79784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan reply.setFormat(format, null); 79884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 79984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 80084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (codec == null) { 80184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan throw new IllegalStateException("Reject SDP: no suitable codecs"); 80284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 80384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan return answer; 80484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 80584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 80684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private SimpleSessionDescription createHoldOffer() { 80784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan SimpleSessionDescription offer = createContinueOffer(); 80884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan offer.setAttribute("sendonly", ""); 80984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan return offer; 81084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 81184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 81284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private SimpleSessionDescription createContinueOffer() { 81384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan SimpleSessionDescription offer = 81484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan new SimpleSessionDescription(mSessionId, getLocalIp()); 81584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Media media = offer.newMedia( 81684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP"); 81784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan AudioCodec codec = mAudioStream.getCodec(); 81884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan media.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp); 81984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan int dtmfType = mAudioStream.getDtmfType(); 82084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (dtmfType != -1) { 82184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan media.setRtpPayload(dtmfType, "telephone-event/8000", "0-15"); 82284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 82384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan return offer; 82484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 82584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 82684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private void grabWifiHighPerfLock() { 82784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (mWifiHighPerfLock == null) { 82884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Log.v(TAG, "acquire wifi high perf lock"); 82984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mWifiHighPerfLock = ((WifiManager) 83084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mContext.getSystemService(Context.WIFI_SERVICE)) 83184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan .createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, TAG); 83284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mWifiHighPerfLock.acquire(); 83384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 83484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 83584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 83684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private void releaseWifiHighPerfLock() { 83784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (mWifiHighPerfLock != null) { 83884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Log.v(TAG, "release wifi high perf lock"); 83984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mWifiHighPerfLock.release(); 84084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mWifiHighPerfLock = null; 84184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 84284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 84384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 84484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private boolean isWifiOn() { 84584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan return (mWm.getConnectionInfo().getBSSID() == null) ? false : true; 84684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 84784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 84884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan /** Toggles mute. */ 84908faac3c26e12863858e1534985dd950193f755fHung-ying Tyan public void toggleMute() { 85008faac3c26e12863858e1534985dd950193f755fHung-ying Tyan synchronized (this) { 851fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan mMuted = !mMuted; 852fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan setAudioGroupMode(); 85384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 85484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 855363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 856363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 857363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Checks if the call is muted. 858363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 859363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @return true if the call is muted 860363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 86108faac3c26e12863858e1534985dd950193f755fHung-ying Tyan public boolean isMuted() { 86208faac3c26e12863858e1534985dd950193f755fHung-ying Tyan synchronized (this) { 86308faac3c26e12863858e1534985dd950193f755fHung-ying Tyan return mMuted; 86408faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } 86584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 86684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 867e87b644402642bad7147f915849bfa0eadaea446Hung-ying Tyan /** 868e87b644402642bad7147f915849bfa0eadaea446Hung-ying Tyan * Puts the device to speaker mode. 86902b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * <p class="note"><strong>Note:</strong> Requires the 87002b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS} permission.</p> 871fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan * 872fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan * @param speakerMode set true to enable speaker mode; false to disable 873e87b644402642bad7147f915849bfa0eadaea446Hung-ying Tyan */ 87408faac3c26e12863858e1534985dd950193f755fHung-ying Tyan public void setSpeakerMode(boolean speakerMode) { 87508faac3c26e12863858e1534985dd950193f755fHung-ying Tyan synchronized (this) { 87608faac3c26e12863858e1534985dd950193f755fHung-ying Tyan ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)) 87708faac3c26e12863858e1534985dd950193f755fHung-ying Tyan .setSpeakerphoneOn(speakerMode); 878fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan setAudioGroupMode(); 87908faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } 88084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 881363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 882fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan private boolean isSpeakerOn() { 883fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan return ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)) 884fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan .isSpeakerphoneOn(); 885fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan } 886fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan 887363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 88802b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2883</a>, 88902b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * event 0--9 maps to decimal 89084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * value 0--9, '*' to 10, '#' to 11, event 'A'--'D' to 12--15, and event 89184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * flash to 16. Currently, event flash is not supported. 892363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 89384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @param code the DTMF code to send. Value 0 to 15 (inclusive) are valid 89484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * inputs. 895363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 89684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void sendDtmf(int code) { 89784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan sendDtmf(code, null); 89884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 899363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 900363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 90102b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2883</a>, 90202b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * event 0--9 maps to decimal 90384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * value 0--9, '*' to 10, '#' to 11, event 'A'--'D' to 12--15, and event 90484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * flash to 16. Currently, event flash is not supported. 905363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 90684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @param code the DTMF code to send. Value 0 to 15 (inclusive) are valid 90784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * inputs. 908363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @param result the result message to send when done 909363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 91008faac3c26e12863858e1534985dd950193f755fHung-ying Tyan public void sendDtmf(int code, Message result) { 91108faac3c26e12863858e1534985dd950193f755fHung-ying Tyan synchronized (this) { 91208faac3c26e12863858e1534985dd950193f755fHung-ying Tyan AudioGroup audioGroup = getAudioGroup(); 91308faac3c26e12863858e1534985dd950193f755fHung-ying Tyan if ((audioGroup != null) && (mSipSession != null) 91408faac3c26e12863858e1534985dd950193f755fHung-ying Tyan && (SipSession.State.IN_CALL == getState())) { 91508faac3c26e12863858e1534985dd950193f755fHung-ying Tyan Log.v(TAG, "send DTMF: " + code); 91608faac3c26e12863858e1534985dd950193f755fHung-ying Tyan audioGroup.sendDtmf(code); 91708faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } 91808faac3c26e12863858e1534985dd950193f755fHung-ying Tyan if (result != null) result.sendToTarget(); 91984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 92084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 921363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 922363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 923363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Gets the {@link AudioStream} object used in this call. The object 924363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * represents the RTP stream that carries the audio data to and from the 925363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * peer. The object may not be created before the call is established. And 926363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * it is undefined after the call ends or the {@link #close} method is 927363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * called. 928363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 929363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @return the {@link AudioStream} object or null if the RTP stream has not 930363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * yet been set up 93184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @hide 932363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 93308faac3c26e12863858e1534985dd950193f755fHung-ying Tyan public AudioStream getAudioStream() { 93408faac3c26e12863858e1534985dd950193f755fHung-ying Tyan synchronized (this) { 93508faac3c26e12863858e1534985dd950193f755fHung-ying Tyan return mAudioStream; 93608faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } 93784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 938363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 939363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 940363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Gets the {@link AudioGroup} object which the {@link AudioStream} object 941363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * joins. The group object may not exist before the call is established. 942363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * Also, the {@code AudioStream} may change its group during a call (e.g., 943363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * after the call is held/un-held). Finally, the {@code AudioGroup} object 944363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * returned by this method is undefined after the call ends or the 9453294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan * {@link #close} method is called. If a group object is set by 9463294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan * {@link #setAudioGroup(AudioGroup)}, then this method returns that object. 947363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * 948363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @return the {@link AudioGroup} object or null if the RTP stream has not 949363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * yet been set up 950363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang * @see #getAudioStream 95184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @hide 952363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 95308faac3c26e12863858e1534985dd950193f755fHung-ying Tyan public AudioGroup getAudioGroup() { 95408faac3c26e12863858e1534985dd950193f755fHung-ying Tyan synchronized (this) { 95508faac3c26e12863858e1534985dd950193f755fHung-ying Tyan if (mAudioGroup != null) return mAudioGroup; 95608faac3c26e12863858e1534985dd950193f755fHung-ying Tyan return ((mAudioStream == null) ? null : mAudioStream.getGroup()); 95708faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } 95884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 959363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 960363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang /** 9613294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan * Sets the {@link AudioGroup} object which the {@link AudioStream} object 9623294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan * joins. If {@code audioGroup} is null, then the {@code AudioGroup} object 963fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan * will be dynamically created when needed. Note that the mode of the 964fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan * {@code AudioGroup} is not changed according to the audio settings (i.e., 965fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan * hold, mute, speaker phone) of this object. This is mainly used to merge 966fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan * multiple {@code SipAudioCall} objects to form a conference call. The 967fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan * settings of the first object (that merges others) override others'. 9683294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan * 9693294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan * @see #getAudioStream 97084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * @hide 9713294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan */ 97208faac3c26e12863858e1534985dd950193f755fHung-ying Tyan public void setAudioGroup(AudioGroup group) { 97308faac3c26e12863858e1534985dd950193f755fHung-ying Tyan synchronized (this) { 97408faac3c26e12863858e1534985dd950193f755fHung-ying Tyan if ((mAudioStream != null) && (mAudioStream.getGroup() != null)) { 97508faac3c26e12863858e1534985dd950193f755fHung-ying Tyan mAudioStream.join(group); 97608faac3c26e12863858e1534985dd950193f755fHung-ying Tyan } 97708faac3c26e12863858e1534985dd950193f755fHung-ying Tyan mAudioGroup = group; 97884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 97984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 9803294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan 9813294d44b96f63f647fba3a03604eb028e28a42bcHung-ying Tyan /** 98284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * Starts the audio for the established call. This method should be called 98384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan * after {@link Listener#onCallEstablished} is called. 98402b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * <p class="note"><strong>Note:</strong> Requires the 985e87b644402642bad7147f915849bfa0eadaea446Hung-ying Tyan * {@link android.Manifest.permission#RECORD_AUDIO}, 986e87b644402642bad7147f915849bfa0eadaea446Hung-ying Tyan * {@link android.Manifest.permission#ACCESS_WIFI_STATE} and 98702b1d685cc287d7c53141872b3d80be4ee5dd59eScott Main * {@link android.Manifest.permission#WAKE_LOCK} permissions.</p> 988363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang */ 98984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan public void startAudio() { 99084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan try { 99184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan startAudioInternal(); 99284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } catch (UnknownHostException e) { 99384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan onError(SipErrorCode.PEER_NOT_REACHABLE, e.getMessage()); 99484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } catch (Throwable e) { 99584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan onError(SipErrorCode.CLIENT_ERROR, e.getMessage()); 99684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 99784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 998363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 99984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private synchronized void startAudioInternal() throws UnknownHostException { 100084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (mPeerSd == null) { 100184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Log.v(TAG, "startAudioInternal() mPeerSd = null"); 100284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan throw new IllegalStateException("mPeerSd = null"); 100384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 1004363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 100584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan stopCall(DONT_RELEASE_SOCKET); 100684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mInCall = true; 1007363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 100884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan // Run exact the same logic in createAnswer() to setup mAudioStream. 100984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan SimpleSessionDescription offer = 101084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan new SimpleSessionDescription(mPeerSd); 101184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan AudioStream stream = mAudioStream; 101284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan AudioCodec codec = null; 101384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan for (Media media : offer.getMedia()) { 101484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if ((codec == null) && (media.getPort() > 0) 101584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan && "audio".equals(media.getType()) 101684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan && "RTP/AVP".equals(media.getProtocol())) { 101784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan // Find the first audio codec we supported. 101884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan for (int type : media.getRtpPayloadTypes()) { 101984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan codec = AudioCodec.getCodec( 102084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan type, media.getRtpmap(type), media.getFmtp(type)); 102184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (codec != null) { 102284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan break; 102384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 102484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 102584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 102684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (codec != null) { 102784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan // Associate with the remote host. 102884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan String address = media.getAddress(); 102984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (address == null) { 103084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan address = offer.getAddress(); 103184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 103284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan stream.associate(InetAddress.getByName(address), 103384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan media.getPort()); 103484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 103584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan stream.setDtmfType(-1); 103684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan stream.setCodec(codec); 103784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan // Check if DTMF is supported in the same media. 103884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan for (int type : media.getRtpPayloadTypes()) { 103984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan String rtpmap = media.getRtpmap(type); 104084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if ((type != codec.type) && (rtpmap != null) 104184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan && rtpmap.startsWith("telephone-event")) { 104284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan stream.setDtmfType(type); 104384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 104484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 104584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 104684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan // Handle recvonly and sendonly. 104784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (mHold) { 104884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan stream.setMode(RtpStream.MODE_NORMAL); 104984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } else if (media.getAttribute("recvonly") != null) { 105084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan stream.setMode(RtpStream.MODE_SEND_ONLY); 105184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } else if(media.getAttribute("sendonly") != null) { 105284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan stream.setMode(RtpStream.MODE_RECEIVE_ONLY); 105384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } else if(offer.getAttribute("recvonly") != null) { 105484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan stream.setMode(RtpStream.MODE_SEND_ONLY); 105584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } else if(offer.getAttribute("sendonly") != null) { 105684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan stream.setMode(RtpStream.MODE_RECEIVE_ONLY); 105784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } else { 105884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan stream.setMode(RtpStream.MODE_NORMAL); 105984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 106084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan break; 106184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 106284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 106384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 106484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (codec == null) { 106584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan throw new IllegalStateException("Reject SDP: no suitable codecs"); 106684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 106784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 106884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (isWifiOn()) grabWifiHighPerfLock(); 106984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 107084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan // AudioGroup logic: 107184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan AudioGroup audioGroup = getAudioGroup(); 107284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (mHold) { 107384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan // don't create an AudioGroup here; doing so will fail if 107484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan // there's another AudioGroup out there that's active 107584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } else { 107684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (audioGroup == null) audioGroup = new AudioGroup(); 107784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan stream.join(audioGroup); 1078fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan } 1079fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan setAudioGroupMode(); 1080fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan } 1081fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan 1082fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan // set audio group mode based on current audio configuration 1083fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan private void setAudioGroupMode() { 1084fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan AudioGroup audioGroup = getAudioGroup(); 1085fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan if (audioGroup != null) { 1086fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan if (mHold) { 1087fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan audioGroup.setMode(AudioGroup.MODE_ON_HOLD); 1088fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan } else if (mMuted) { 108984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan audioGroup.setMode(AudioGroup.MODE_MUTED); 1090fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan } else if (isSpeakerOn()) { 1091fa81463e88d15859b557be6fef5982b049b92ab8Hung-ying Tyan audioGroup.setMode(AudioGroup.MODE_ECHO_SUPPRESSION); 109284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } else { 109384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan audioGroup.setMode(AudioGroup.MODE_NORMAL); 109484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 109584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 109684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 109784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 109884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private void stopCall(boolean releaseSocket) { 109984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan Log.d(TAG, "stop audiocall"); 110084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan releaseWifiHighPerfLock(); 110184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (mAudioStream != null) { 110284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mAudioStream.join(null); 110384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 110484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (releaseSocket) { 110584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mAudioStream.release(); 110684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan mAudioStream = null; 110784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 110884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 110984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 111084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 111184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private String getLocalIp() { 111284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan return mSipSession.getLocalIp(); 111384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 1114363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang 111584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private void throwSipException(Throwable throwable) throws SipException { 111684a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan if (throwable instanceof SipException) { 111784a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan throw (SipException) throwable; 111884a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } else { 111984a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan throw new SipException("", throwable); 112084a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 112184a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 112284a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan 112384a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan private SipProfile getPeerProfile(SipSession session) { 112484a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan return session.getPeerProfile(); 112584a357bb6a8005e1c5e924e96a8ecf310e77c47cHung-ying Tyan } 1126363c2ab82cca4f095e9e0c8465e28f6d27a24bf8Chung-yih Wang} 1127