198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang/*
298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * Copyright (C) 2010 The Android Open Source Project
398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang *
498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * Licensed under the Apache License, Version 2.0 (the "License");
598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * you may not use this file except in compliance with the License.
698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * You may obtain a copy of the License at
798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang *
898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang *      http://www.apache.org/licenses/LICENSE-2.0
998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang *
1098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * Unless required by applicable law or agreed to in writing, software
1198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * distributed under the License is distributed on an "AS IS" BASIS,
1298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * See the License for the specific language governing permissions and
1498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * limitations under the License.
1598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang */
1698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
1798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangpackage android.net.sip;
1898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
193adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyanimport android.content.Context;
203adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyanimport android.media.AudioManager;
213adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyanimport android.net.rtp.AudioCodec;
2298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport android.net.rtp.AudioGroup;
2398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport android.net.rtp.AudioStream;
243adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyanimport android.net.rtp.RtpStream;
253adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyanimport android.net.sip.SimpleSessionDescription.Media;
263adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyanimport android.net.wifi.WifiManager;
2798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport android.os.Message;
289329db04f13480ccdff013dcc00cdb96f12a921cWink Savilleimport android.telephony.Rlog;
29bb18b405c539e483cce67ae207bd7e6263c0d071repo syncimport android.text.TextUtils;
303adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyanimport java.io.IOException;
313adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyanimport java.net.InetAddress;
323adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyanimport java.net.UnknownHostException;
3398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
3498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang/**
3516b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main * Handles an Internet audio call over SIP. You can instantiate this class with {@link SipManager},
3616b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main * using {@link SipManager#makeAudioCall makeAudioCall()} and  {@link SipManager#takeAudioCall
3716b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main * takeAudioCall()}.
38d3666076203ecc553211f8fa443ef259667ddd5eHung-ying Tyan *
3916b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main * <p class="note"><strong>Note:</strong> Using this class require the
40d3666076203ecc553211f8fa443ef259667ddd5eHung-ying Tyan *   {@link android.Manifest.permission#INTERNET} and
41b4074803f775e5a4616287804f3405fd17eecab3Joe Fernandez *   {@link android.Manifest.permission#USE_SIP} permissions. In addition, {@link
4216b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main *   #startAudio} requires the
43d3666076203ecc553211f8fa443ef259667ddd5eHung-ying Tyan *   {@link android.Manifest.permission#RECORD_AUDIO},
4416b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main *   {@link android.Manifest.permission#ACCESS_WIFI_STATE}, and
4516b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main *   {@link android.Manifest.permission#WAKE_LOCK} permissions; and {@link #setSpeakerMode
4616b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main *   setSpeakerMode()} requires the
4716b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main *   {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS} permission.</p>
48b4074803f775e5a4616287804f3405fd17eecab3Joe Fernandez *
49b4074803f775e5a4616287804f3405fd17eecab3Joe Fernandez * <div class="special reference">
50b4074803f775e5a4616287804f3405fd17eecab3Joe Fernandez * <h3>Developer Guides</h3>
51b4074803f775e5a4616287804f3405fd17eecab3Joe Fernandez * <p>For more information about using SIP, read the
52b4074803f775e5a4616287804f3405fd17eecab3Joe Fernandez * <a href="{@docRoot}guide/topics/network/sip.html">Session Initiation Protocol</a>
53b4074803f775e5a4616287804f3405fd17eecab3Joe Fernandez * developer guide.</p>
54b4074803f775e5a4616287804f3405fd17eecab3Joe Fernandez * </div>
5598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang */
567a4ec7268fbfb56e22f1cb8497d37ed733d8aa8eHung-ying Tyanpublic class SipAudioCall {
579329db04f13480ccdff013dcc00cdb96f12a921cWink Saville    private static final String LOG_TAG = SipAudioCall.class.getSimpleName();
589329db04f13480ccdff013dcc00cdb96f12a921cWink Saville    private static final boolean DBG = true;
593adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private static final boolean RELEASE_SOCKET = true;
603adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private static final boolean DONT_RELEASE_SOCKET = false;
613adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private static final int SESSION_TIMEOUT = 5; // in seconds
62f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync    private static final int TRANSFER_TIMEOUT = 15; // in seconds
633adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
6416b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main    /** Listener for events relating to a SIP call, such as when a call is being
6516b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main     * recieved ("on ringing") or a call is outgoing ("on calling").
6616b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main     * <p>Many of these events are also received by {@link SipSession.Listener}.</p>
6716b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main     */
683adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    public static class Listener {
6998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang        /**
7098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         * Called when the call object is ready to make another call.
714e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan         * The default implementation calls {@link #onChanged}.
7298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         *
7398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         * @param call the call object that is ready to make another call
7498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         */
753adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        public void onReadyToCall(SipAudioCall call) {
763adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            onChanged(call);
773adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
7898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
7998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang        /**
8098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         * Called when a request is sent out to initiate a new call.
814e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan         * The default implementation calls {@link #onChanged}.
8298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         *
8398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         * @param call the call object that carries out the audio call
8498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         */
853adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        public void onCalling(SipAudioCall call) {
863adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            onChanged(call);
873adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
8898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
8998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang        /**
9098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         * Called when a new call comes in.
914e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan         * The default implementation calls {@link #onChanged}.
9298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         *
9398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         * @param call the call object that carries out the audio call
9498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         * @param caller the SIP profile of the caller
9598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         */
963adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        public void onRinging(SipAudioCall call, SipProfile caller) {
973adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            onChanged(call);
983adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
9998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
10098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang        /**
1013adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan         * Called when a RINGING response is received for the INVITE request
1024e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan         * sent. The default implementation calls {@link #onChanged}.
10398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         *
10498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         * @param call the call object that carries out the audio call
10598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         */
1063adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        public void onRingingBack(SipAudioCall call) {
1073adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            onChanged(call);
1083adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
10998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
11098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang        /**
11198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         * Called when the session is established.
1124e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan         * The default implementation calls {@link #onChanged}.
11398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         *
11498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         * @param call the call object that carries out the audio call
11598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         */
1163adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        public void onCallEstablished(SipAudioCall call) {
1173adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            onChanged(call);
1183adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
11998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
12098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang        /**
12198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         * Called when the session is terminated.
1224e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan         * The default implementation calls {@link #onChanged}.
12398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         *
12498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         * @param call the call object that carries out the audio call
12598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         */
1263adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        public void onCallEnded(SipAudioCall call) {
1273adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            onChanged(call);
1283adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
12998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
13098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang        /**
13198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         * Called when the peer is busy during session initialization.
1324e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan         * The default implementation calls {@link #onChanged}.
13398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         *
13498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         * @param call the call object that carries out the audio call
13598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         */
1363adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        public void onCallBusy(SipAudioCall call) {
1373adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            onChanged(call);
1383adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
13998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
14098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang        /**
14198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         * Called when the call is on hold.
1424e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan         * The default implementation calls {@link #onChanged}.
14398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         *
14498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         * @param call the call object that carries out the audio call
14598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         */
1463adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        public void onCallHeld(SipAudioCall call) {
1473adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            onChanged(call);
1483adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
14998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
15098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang        /**
1513adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan         * Called when an error occurs. The default implementation is no op.
15298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         *
15398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         * @param call the call object that carries out the audio call
154188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan         * @param errorCode error code of this error
15598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         * @param errorMessage error message
156a0171082cfc4b860a82dcf5ebbd498b253f1032fHung-ying Tyan         * @see SipErrorCode
15798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang         */
1583adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        public void onError(SipAudioCall call, int errorCode,
1593adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                String errorMessage) {
1603adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            // no-op
1613adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
1623adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
1633adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        /**
1643adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan         * Called when an event occurs and the corresponding callback is not
1653adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan         * overridden. The default implementation is no op. Error events are
1663adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan         * not re-directed to this callback and are handled in {@link #onError}.
1673adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan         */
1683adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        public void onChanged(SipAudioCall call) {
1693adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            // no-op
1703adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
17198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang    }
17298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
1733adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private Context mContext;
1743adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private SipProfile mLocalProfile;
1753adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private SipAudioCall.Listener mListener;
1763adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private SipSession mSipSession;
17722ecc3df834674605daf86f7edf20169b6ca800brepo sync    private SipSession mTransferringSession;
1783adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
1793adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private long mSessionId = System.currentTimeMillis();
1803adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private String mPeerSd;
1813adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
1823adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private AudioStream mAudioStream;
1833adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private AudioGroup mAudioGroup;
1843adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
1853adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private boolean mInCall = false;
1863adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private boolean mMuted = false;
1873adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private boolean mHold = false;
1883adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
1893adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private WifiManager mWm;
1903adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private WifiManager.WifiLock mWifiHighPerfLock;
1913adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
1923adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private int mErrorCode = SipErrorCode.NO_ERROR;
1933adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private String mErrorMessage;
1943adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
19598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang    /**
1963adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * Creates a call object with the local SIP profile.
1973adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @param context the context for accessing system services such as
1983adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     *        ringtone, audio, WIFI etc
19998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     */
2003adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    public SipAudioCall(Context context, SipProfile localProfile) {
2013adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        mContext = context;
2023adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        mLocalProfile = localProfile;
2033adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        mWm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
20498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang    }
20598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
20698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang    /**
20798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * Sets the listener to listen to the audio call events. The method calls
20816b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main     * {@link #setListener setListener(listener, false)}.
20998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     *
21098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * @param listener to listen to the audio call events of this object
21198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * @see #setListener(Listener, boolean)
21298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     */
2133adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    public void setListener(SipAudioCall.Listener listener) {
2143adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        setListener(listener, false);
2153adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
21698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
21798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang    /**
21898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * Sets the listener to listen to the audio call events. A
21998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * {@link SipAudioCall} can only hold one listener at a time. Subsequent
22098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * calls to this method override the previous listener.
22198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     *
22298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * @param listener to listen to the audio call events of this object
22398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * @param callbackImmediately set to true if the caller wants to be called
22498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     *      back immediately on the current state
22598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     */
2263adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    public void setListener(SipAudioCall.Listener listener,
2273adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            boolean callbackImmediately) {
2283adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        mListener = listener;
2293adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        try {
2303adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            if ((listener == null) || !callbackImmediately) {
2313adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                // do nothing
2323adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            } else if (mErrorCode != SipErrorCode.NO_ERROR) {
2333adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                listener.onError(this, mErrorCode, mErrorMessage);
2343adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            } else if (mInCall) {
2353adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                if (mHold) {
2363adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    listener.onCallHeld(this);
2373adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                } else {
2383adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    listener.onCallEstablished(this);
2393adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                }
2403adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            } else {
2413adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                int state = getState();
2423adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                switch (state) {
2433adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    case SipSession.State.READY_TO_CALL:
2443adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        listener.onReadyToCall(this);
2453adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        break;
2463adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    case SipSession.State.INCOMING_CALL:
2473adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        listener.onRinging(this, getPeerProfile());
2483adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        break;
2493adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    case SipSession.State.OUTGOING_CALL:
2503adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        listener.onCalling(this);
2513adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        break;
2523adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    case SipSession.State.OUTGOING_CALL_RING_BACK:
2533adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        listener.onRingingBack(this);
2543adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        break;
2553adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                }
2563adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            }
2573adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        } catch (Throwable t) {
2589329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            loge("setListener()", t);
2593adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
2603adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
2613adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
2623adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    /**
2633adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * Checks if the call is established.
2643adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     *
2653adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @return true if the call is established
2663adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     */
2674e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan    public boolean isInCall() {
2684e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        synchronized (this) {
2694e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            return mInCall;
2704e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        }
2713adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
2723adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
2733adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    /**
2743adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * Checks if the call is on hold.
2753adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     *
2763adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @return true if the call is on hold
2773adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     */
2784e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan    public boolean isOnHold() {
2794e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        synchronized (this) {
2804e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            return mHold;
2814e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        }
2823adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
28398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
28498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang    /**
285ecd6e06707e7ce1001a05e919063da1c4b10d746Hung-ying Tyan     * Closes this object. This object is not usable after being closed.
28698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     */
2873adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    public void close() {
2883adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        close(true);
2893adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
2903adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
2913adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private synchronized void close(boolean closeRtp) {
2923adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        if (closeRtp) stopCall(RELEASE_SOCKET);
2933adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
2943adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        mInCall = false;
2953adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        mHold = false;
2963adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        mSessionId = System.currentTimeMillis();
2973adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        mErrorCode = SipErrorCode.NO_ERROR;
2983adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        mErrorMessage = null;
2993adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
3003adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        if (mSipSession != null) {
3013adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            mSipSession.setListener(null);
3023adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            mSipSession = null;
3033adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
3043adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
30598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
30698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang    /**
3073adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * Gets the local SIP profile.
30898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     *
3093adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @return the local SIP profile
31098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     */
3114e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan    public SipProfile getLocalProfile() {
3124e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        synchronized (this) {
3134e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            return mLocalProfile;
3144e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        }
3153adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
31698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
31798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang    /**
3183adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * Gets the peer's SIP profile.
3193adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     *
3203adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @return the peer's SIP profile
321ecd6e06707e7ce1001a05e919063da1c4b10d746Hung-ying Tyan     */
3224e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan    public SipProfile getPeerProfile() {
3234e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        synchronized (this) {
3244e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            return (mSipSession == null) ? null : mSipSession.getPeerProfile();
3254e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        }
3263adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
3273adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
3283adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    /**
3293adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * Gets the state of the {@link SipSession} that carries this call.
3303adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * The value returned must be one of the states in {@link SipSession.State}.
3313adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     *
3323adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @return the session state
3333adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     */
3344e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan    public int getState() {
3354e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        synchronized (this) {
3364e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            if (mSipSession == null) return SipSession.State.READY_TO_CALL;
3374e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            return mSipSession.getState();
3384e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        }
3393adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
3403adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
3413adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
3423adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    /**
3433adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * Gets the {@link SipSession} that carries this call.
3443adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     *
3453adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @return the session object that carries this call
3463adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @hide
3473adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     */
3484e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan    public SipSession getSipSession() {
3494e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        synchronized (this) {
3504e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            return mSipSession;
3514e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        }
3523adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
3533adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
35422ecc3df834674605daf86f7edf20169b6ca800brepo sync    private synchronized void transferToNewSession() {
35522ecc3df834674605daf86f7edf20169b6ca800brepo sync        if (mTransferringSession == null) return;
35622ecc3df834674605daf86f7edf20169b6ca800brepo sync        SipSession origin = mSipSession;
35722ecc3df834674605daf86f7edf20169b6ca800brepo sync        mSipSession = mTransferringSession;
35822ecc3df834674605daf86f7edf20169b6ca800brepo sync        mTransferringSession = null;
35922ecc3df834674605daf86f7edf20169b6ca800brepo sync
36022ecc3df834674605daf86f7edf20169b6ca800brepo sync        // stop the replaced call.
36122ecc3df834674605daf86f7edf20169b6ca800brepo sync        if (mAudioStream != null) {
36222ecc3df834674605daf86f7edf20169b6ca800brepo sync            mAudioStream.join(null);
36322ecc3df834674605daf86f7edf20169b6ca800brepo sync        } else {
36422ecc3df834674605daf86f7edf20169b6ca800brepo sync            try {
36522ecc3df834674605daf86f7edf20169b6ca800brepo sync                mAudioStream = new AudioStream(InetAddress.getByName(
36622ecc3df834674605daf86f7edf20169b6ca800brepo sync                        getLocalIp()));
36722ecc3df834674605daf86f7edf20169b6ca800brepo sync            } catch (Throwable t) {
3689329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                loge("transferToNewSession():", t);
36922ecc3df834674605daf86f7edf20169b6ca800brepo sync            }
37022ecc3df834674605daf86f7edf20169b6ca800brepo sync        }
37122ecc3df834674605daf86f7edf20169b6ca800brepo sync        if (origin != null) origin.endCall();
37222ecc3df834674605daf86f7edf20169b6ca800brepo sync        startAudio();
37322ecc3df834674605daf86f7edf20169b6ca800brepo sync    }
37422ecc3df834674605daf86f7edf20169b6ca800brepo sync
3753adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private SipSession.Listener createListener() {
3763adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        return new SipSession.Listener() {
3773adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            @Override
3783adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            public void onCalling(SipSession session) {
3799329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                if (DBG) log("onCalling: session=" + session);
3803adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                Listener listener = mListener;
3813adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                if (listener != null) {
3823adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    try {
3833adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        listener.onCalling(SipAudioCall.this);
3843adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    } catch (Throwable t) {
3859329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                        loge("onCalling():", t);
3863adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    }
3873adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                }
3883adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            }
3893adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
3903adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            @Override
3913adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            public void onRingingBack(SipSession session) {
3929329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                if (DBG) log("onRingingBackk: " + session);
3933adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                Listener listener = mListener;
3943adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                if (listener != null) {
3953adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    try {
3963adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        listener.onRingingBack(SipAudioCall.this);
3973adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    } catch (Throwable t) {
3989329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                        loge("onRingingBack():", t);
3993adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    }
4003adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                }
4013adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            }
4023adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
4033adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            @Override
4044e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            public void onRinging(SipSession session,
4053adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    SipProfile peerProfile, String sessionDescription) {
406bb18b405c539e483cce67ae207bd7e6263c0d071repo sync                // this callback is triggered only for reinvite.
4074e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                synchronized (SipAudioCall.this) {
4084e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                    if ((mSipSession == null) || !mInCall
4094e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                            || !session.getCallId().equals(
4104e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                                    mSipSession.getCallId())) {
4114e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                        // should not happen
4124e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                        session.endCall();
4134e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                        return;
4144e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                    }
4153adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
4164e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                    // session changing request
4174e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                    try {
4184e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                        String answer = createAnswer(sessionDescription).encode();
4194e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                        mSipSession.answerCall(answer, SESSION_TIMEOUT);
4204e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                    } catch (Throwable e) {
4219329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                        loge("onRinging():", e);
4224e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                        session.endCall();
4234e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                    }
4243adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                }
4253adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            }
4263adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
4273adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            @Override
4283adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            public void onCallEstablished(SipSession session,
4293adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    String sessionDescription) {
4303adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                mPeerSd = sessionDescription;
4319329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                if (DBG) log("onCallEstablished(): " + mPeerSd);
4323adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
43322ecc3df834674605daf86f7edf20169b6ca800brepo sync                // TODO: how to notify the UI that the remote party is changed
43422ecc3df834674605daf86f7edf20169b6ca800brepo sync                if ((mTransferringSession != null)
43522ecc3df834674605daf86f7edf20169b6ca800brepo sync                        && (session == mTransferringSession)) {
43622ecc3df834674605daf86f7edf20169b6ca800brepo sync                    transferToNewSession();
43722ecc3df834674605daf86f7edf20169b6ca800brepo sync                    return;
43822ecc3df834674605daf86f7edf20169b6ca800brepo sync                }
43922ecc3df834674605daf86f7edf20169b6ca800brepo sync
4403adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                Listener listener = mListener;
4413adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                if (listener != null) {
4423adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    try {
4433adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        if (mHold) {
4443adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                            listener.onCallHeld(SipAudioCall.this);
4453adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        } else {
4463adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                            listener.onCallEstablished(SipAudioCall.this);
4473adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        }
4483adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    } catch (Throwable t) {
4499329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                        loge("onCallEstablished(): ", t);
4503adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    }
4513adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                }
4523adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            }
4533adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
4543adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            @Override
4553adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            public void onCallEnded(SipSession session) {
4569329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                if (DBG) log("onCallEnded: " + session + " mSipSession:" + mSipSession);
45722ecc3df834674605daf86f7edf20169b6ca800brepo sync                // reset the trasnferring session if it is the one.
45822ecc3df834674605daf86f7edf20169b6ca800brepo sync                if (session == mTransferringSession) {
45922ecc3df834674605daf86f7edf20169b6ca800brepo sync                    mTransferringSession = null;
46022ecc3df834674605daf86f7edf20169b6ca800brepo sync                    return;
46122ecc3df834674605daf86f7edf20169b6ca800brepo sync                }
46222ecc3df834674605daf86f7edf20169b6ca800brepo sync                // or ignore the event if the original session is being
46322ecc3df834674605daf86f7edf20169b6ca800brepo sync                // transferred to the new one.
46422ecc3df834674605daf86f7edf20169b6ca800brepo sync                if ((mTransferringSession != null) ||
46522ecc3df834674605daf86f7edf20169b6ca800brepo sync                    (session != mSipSession)) return;
46622ecc3df834674605daf86f7edf20169b6ca800brepo sync
4673adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                Listener listener = mListener;
4683adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                if (listener != null) {
4693adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    try {
4703adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        listener.onCallEnded(SipAudioCall.this);
4713adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    } catch (Throwable t) {
4729329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                        loge("onCallEnded(): ", t);
4733adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    }
4743adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                }
4753adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                close();
4763adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            }
4773adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
4783adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            @Override
4793adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            public void onCallBusy(SipSession session) {
4809329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                if (DBG) log("onCallBusy: " + session);
4813adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                Listener listener = mListener;
4823adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                if (listener != null) {
4833adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    try {
4843adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        listener.onCallBusy(SipAudioCall.this);
4853adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    } catch (Throwable t) {
4869329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                        loge("onCallBusy(): ", t);
4873adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    }
4883adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                }
4893adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                close(false);
4903adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            }
4913adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
4923adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            @Override
4933adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            public void onCallChangeFailed(SipSession session, int errorCode,
4943adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    String message) {
4959329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                if (DBG) log("onCallChangedFailed: " + message);
4963adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                mErrorCode = errorCode;
4973adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                mErrorMessage = message;
4983adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                Listener listener = mListener;
4993adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                if (listener != null) {
5003adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    try {
5013adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        listener.onError(SipAudioCall.this, mErrorCode,
5023adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                                message);
5033adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    } catch (Throwable t) {
5049329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                        loge("onCallBusy():", t);
5053adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    }
5063adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                }
5073adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            }
5083adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
5093adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            @Override
5103adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            public void onError(SipSession session, int errorCode,
5113adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    String message) {
5123adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                SipAudioCall.this.onError(errorCode, message);
5133adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            }
5143adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
5153adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            @Override
5163adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            public void onRegistering(SipSession session) {
5173adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                // irrelevant
5183adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            }
5193adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
5203adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            @Override
5213adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            public void onRegistrationTimeout(SipSession session) {
5223adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                // irrelevant
5233adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            }
5243adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
5253adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            @Override
5263adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            public void onRegistrationFailed(SipSession session, int errorCode,
5273adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    String message) {
5283adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                // irrelevant
5293adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            }
5303adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
5313adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            @Override
5323adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            public void onRegistrationDone(SipSession session, int duration) {
5333adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                // irrelevant
5343adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            }
53522ecc3df834674605daf86f7edf20169b6ca800brepo sync
53622ecc3df834674605daf86f7edf20169b6ca800brepo sync            @Override
53722ecc3df834674605daf86f7edf20169b6ca800brepo sync            public void onCallTransferring(SipSession newSession,
53822ecc3df834674605daf86f7edf20169b6ca800brepo sync                    String sessionDescription) {
5399329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                if (DBG) log("onCallTransferring: mSipSession="
5409329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                        + mSipSession + " newSession=" + newSession);
54122ecc3df834674605daf86f7edf20169b6ca800brepo sync                mTransferringSession = newSession;
54222ecc3df834674605daf86f7edf20169b6ca800brepo sync                try {
543f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                    if (sessionDescription == null) {
544f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                        newSession.makeCall(newSession.getPeerProfile(),
545f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                                createOffer().encode(), TRANSFER_TIMEOUT);
546f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                    } else {
547f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                        String answer = createAnswer(sessionDescription).encode();
548f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                        newSession.answerCall(answer, SESSION_TIMEOUT);
549f0af349a5b7b7df47e6b1b53e028cecdff50caa6repo sync                    }
55022ecc3df834674605daf86f7edf20169b6ca800brepo sync                } catch (Throwable e) {
5519329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                    loge("onCallTransferring()", e);
55222ecc3df834674605daf86f7edf20169b6ca800brepo sync                    newSession.endCall();
55322ecc3df834674605daf86f7edf20169b6ca800brepo sync                }
55422ecc3df834674605daf86f7edf20169b6ca800brepo sync            }
5553adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        };
5563adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
5573adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
5583adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private void onError(int errorCode, String message) {
5599329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        if (DBG) log("onError: "
5603adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                + SipErrorCode.toString(errorCode) + ": " + message);
5613adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        mErrorCode = errorCode;
5623adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        mErrorMessage = message;
5633adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        Listener listener = mListener;
5643adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        if (listener != null) {
5653adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            try {
5663adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                listener.onError(this, errorCode, message);
5673adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            } catch (Throwable t) {
5689329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                loge("onError():", t);
5693adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            }
5703adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
5713adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        synchronized (this) {
5723adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            if ((errorCode == SipErrorCode.DATA_CONNECTION_LOST)
5733adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    || !isInCall()) {
5743adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                close(true);
5753adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            }
5763adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
5773adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
578ecd6e06707e7ce1001a05e919063da1c4b10d746Hung-ying Tyan
579ecd6e06707e7ce1001a05e919063da1c4b10d746Hung-ying Tyan    /**
58098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * Attaches an incoming call to this call object.
58198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     *
58298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * @param session the session that receives the incoming call
5836d0ef774a2492b0996ded3a43c300c7f72a94897Chia-chi Yeh     * @param sessionDescription the session description of the incoming call
5843adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @throws SipException if the SIP service fails to attach this object to
58538ee75e8c0722118d26a23afe5b509b6a09302f4Hung-ying Tyan     *        the session or VOIP API is not supported by the device
58638ee75e8c0722118d26a23afe5b509b6a09302f4Hung-ying Tyan     * @see SipManager#isVoipSupported
5873adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     */
5884e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan    public void attachCall(SipSession session, String sessionDescription)
5894e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            throws SipException {
59038ee75e8c0722118d26a23afe5b509b6a09302f4Hung-ying Tyan        if (!SipManager.isVoipSupported(mContext)) {
59138ee75e8c0722118d26a23afe5b509b6a09302f4Hung-ying Tyan            throw new SipException("VOIP API is not supported");
59238ee75e8c0722118d26a23afe5b509b6a09302f4Hung-ying Tyan        }
59338ee75e8c0722118d26a23afe5b509b6a09302f4Hung-ying Tyan
5944e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        synchronized (this) {
5954e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            mSipSession = session;
5964e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            mPeerSd = sessionDescription;
5979329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            if (DBG) log("attachCall(): " + mPeerSd);
5984e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            try {
5994e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                session.setListener(createListener());
6004e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            } catch (Throwable e) {
6019329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                loge("attachCall()", e);
6024e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                throwSipException(e);
6034e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            }
6043adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
6053adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
6063adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
6073adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    /**
6083adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * Initiates an audio call to the specified profile. The attempt will be
6093adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * timed out if the call is not established within {@code timeout} seconds
61016b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main     * and {@link Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
6113adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * will be called.
6123adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     *
6134e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan     * @param peerProfile the SIP profile to make the call to
6147a4ec7268fbfb56e22f1cb8497d37ed733d8aa8eHung-ying Tyan     * @param sipSession the {@link SipSession} for carrying out the call
6153adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @param timeout the timeout value in seconds. Default value (defined by
6163adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     *        SIP protocol) is used if {@code timeout} is zero or negative.
61716b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main     * @see Listener#onError
6183adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @throws SipException if the SIP service fails to create a session for the
61938ee75e8c0722118d26a23afe5b509b6a09302f4Hung-ying Tyan     *        call or VOIP API is not supported by the device
62038ee75e8c0722118d26a23afe5b509b6a09302f4Hung-ying Tyan     * @see SipManager#isVoipSupported
62198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     */
6224e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan    public void makeCall(SipProfile peerProfile, SipSession sipSession,
6234e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            int timeout) throws SipException {
6249329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        if (DBG) log("makeCall: " + peerProfile + " session=" + sipSession + " timeout=" + timeout);
62538ee75e8c0722118d26a23afe5b509b6a09302f4Hung-ying Tyan        if (!SipManager.isVoipSupported(mContext)) {
62638ee75e8c0722118d26a23afe5b509b6a09302f4Hung-ying Tyan            throw new SipException("VOIP API is not supported");
62738ee75e8c0722118d26a23afe5b509b6a09302f4Hung-ying Tyan        }
62838ee75e8c0722118d26a23afe5b509b6a09302f4Hung-ying Tyan
6294e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        synchronized (this) {
6304e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            mSipSession = sipSession;
6314e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            try {
6324e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                mAudioStream = new AudioStream(InetAddress.getByName(
6334e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                        getLocalIp()));
6344e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                sipSession.setListener(createListener());
6354e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                sipSession.makeCall(peerProfile, createOffer().encode(),
6364e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                        timeout);
6374e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            } catch (IOException e) {
6389329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                loge("makeCall:", e);
6394e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                throw new SipException("makeCall()", e);
6404e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            }
6413adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
6423adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
64398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
6443adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    /**
6453adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * Ends a call.
6463adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @throws SipException if the SIP service fails to end the call
6473adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     */
6484e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan    public void endCall() throws SipException {
6499329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        if (DBG) log("endCall: mSipSession" + mSipSession);
6504e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        synchronized (this) {
6514e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            stopCall(RELEASE_SOCKET);
6524e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            mInCall = false;
6533adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
6544e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            // perform the above local ops first and then network op
6554e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            if (mSipSession != null) mSipSession.endCall();
6564e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        }
6573adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
65898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
65998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang    /**
660901503e1cf8b6cfac1540a22c325eb5cdd879429Hung-ying Tyan     * Puts a call on hold.  When succeeds, {@link Listener#onCallHeld} is
661f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan     * called. The attempt will be timed out if the call is not established
662f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan     * within {@code timeout} seconds and
66316b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main     * {@link Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
664f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan     * will be called.
665f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan     *
6663adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @param timeout the timeout value in seconds. Default value (defined by
6673adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     *        SIP protocol) is used if {@code timeout} is zero or negative.
66816b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main     * @see Listener#onError
6693adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @throws SipException if the SIP service fails to hold the call
67098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     */
6714e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan    public void holdCall(int timeout) throws SipException {
6729329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        if (DBG) log("holdCall: mSipSession" + mSipSession + " timeout=" + timeout);
6734e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        synchronized (this) {
67444afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan            if (mHold) return;
67538ee75e8c0722118d26a23afe5b509b6a09302f4Hung-ying Tyan            if (mSipSession == null) {
6769329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                loge("holdCall:");
67738ee75e8c0722118d26a23afe5b509b6a09302f4Hung-ying Tyan                throw new SipException("Not in a call to hold call");
67838ee75e8c0722118d26a23afe5b509b6a09302f4Hung-ying Tyan            }
6794e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            mSipSession.changeCall(createHoldOffer().encode(), timeout);
6804e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            mHold = true;
68144afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan            setAudioGroupMode();
6824e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        }
6833adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
68498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
685f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan    /**
686f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan     * Answers a call. The attempt will be timed out if the call is not
687f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan     * established within {@code timeout} seconds and
68816b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main     * {@link Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
689f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan     * will be called.
690f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan     *
6913adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @param timeout the timeout value in seconds. Default value (defined by
6923adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     *        SIP protocol) is used if {@code timeout} is zero or negative.
69316b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main     * @see Listener#onError
6943adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @throws SipException if the SIP service fails to answer the call
695f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan     */
6964e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan    public void answerCall(int timeout) throws SipException {
6979329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        if (DBG) log("answerCall: mSipSession" + mSipSession + " timeout=" + timeout);
6984e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        synchronized (this) {
69938ee75e8c0722118d26a23afe5b509b6a09302f4Hung-ying Tyan            if (mSipSession == null) {
70038ee75e8c0722118d26a23afe5b509b6a09302f4Hung-ying Tyan                throw new SipException("No call to answer");
70138ee75e8c0722118d26a23afe5b509b6a09302f4Hung-ying Tyan            }
7024e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            try {
7034e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                mAudioStream = new AudioStream(InetAddress.getByName(
7044e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                        getLocalIp()));
7054e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                mSipSession.answerCall(createAnswer(mPeerSd).encode(), timeout);
7064e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            } catch (IOException e) {
7079329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                loge("answerCall:", e);
7084e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                throw new SipException("answerCall()", e);
7094e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            }
7103adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
7113adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
71298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
71398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang    /**
71498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * Continues a call that's on hold. When succeeds,
715f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan     * {@link Listener#onCallEstablished} is called. The attempt will be timed
716f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan     * out if the call is not established within {@code timeout} seconds and
71716b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main     * {@link Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
718f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan     * will be called.
719f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan     *
7203adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @param timeout the timeout value in seconds. Default value (defined by
7213adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     *        SIP protocol) is used if {@code timeout} is zero or negative.
72216b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main     * @see Listener#onError
7233adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @throws SipException if the SIP service fails to unhold the call
72498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     */
7254e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan    public void continueCall(int timeout) throws SipException {
7269329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        if (DBG) log("continueCall: mSipSession" + mSipSession + " timeout=" + timeout);
7274e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        synchronized (this) {
7284e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            if (!mHold) return;
7294e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            mSipSession.changeCall(createContinueOffer().encode(), timeout);
7304e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            mHold = false;
73144afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan            setAudioGroupMode();
7324e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        }
7333adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
73498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
7353adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private SimpleSessionDescription createOffer() {
7363adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        SimpleSessionDescription offer =
7373adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                new SimpleSessionDescription(mSessionId, getLocalIp());
7383adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        AudioCodec[] codecs = AudioCodec.getCodecs();
7393adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        Media media = offer.newMedia(
7403adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
7413adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        for (AudioCodec codec : AudioCodec.getCodecs()) {
7423adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            media.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
7433adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
7443adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        media.setRtpPayload(127, "telephone-event/8000", "0-15");
7459329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        if (DBG) log("createOffer: offer=" + offer);
7463adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        return offer;
7473adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
74898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
7493adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private SimpleSessionDescription createAnswer(String offerSd) {
750bb18b405c539e483cce67ae207bd7e6263c0d071repo sync        if (TextUtils.isEmpty(offerSd)) return createOffer();
7513adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        SimpleSessionDescription offer =
7523adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                new SimpleSessionDescription(offerSd);
7533adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        SimpleSessionDescription answer =
7543adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                new SimpleSessionDescription(mSessionId, getLocalIp());
7553adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        AudioCodec codec = null;
7563adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        for (Media media : offer.getMedia()) {
7573adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            if ((codec == null) && (media.getPort() > 0)
7583adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    && "audio".equals(media.getType())
7593adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    && "RTP/AVP".equals(media.getProtocol())) {
7603adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                // Find the first audio codec we supported.
7613adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                for (int type : media.getRtpPayloadTypes()) {
7623adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    codec = AudioCodec.getCodec(type, media.getRtpmap(type),
7633adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                            media.getFmtp(type));
7643adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    if (codec != null) {
7653adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        break;
7663adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    }
7673adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                }
7683adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                if (codec != null) {
7693adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    Media reply = answer.newMedia(
7703adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                            "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
7713adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    reply.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
77298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
7733adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    // Check if DTMF is supported in the same media.
7743adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    for (int type : media.getRtpPayloadTypes()) {
7753adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        String rtpmap = media.getRtpmap(type);
7763adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        if ((type != codec.type) && (rtpmap != null)
7773adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                                && rtpmap.startsWith("telephone-event")) {
7783adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                            reply.setRtpPayload(
7793adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                                    type, rtpmap, media.getFmtp(type));
7803adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        }
7813adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    }
7823adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
7833adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    // Handle recvonly and sendonly.
7843adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    if (media.getAttribute("recvonly") != null) {
7853adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        answer.setAttribute("sendonly", "");
7863adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    } else if(media.getAttribute("sendonly") != null) {
7873adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        answer.setAttribute("recvonly", "");
7883adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    } else if(offer.getAttribute("recvonly") != null) {
7893adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        answer.setAttribute("sendonly", "");
7903adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    } else if(offer.getAttribute("sendonly") != null) {
7913adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        answer.setAttribute("recvonly", "");
7923adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    }
7933adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    continue;
7943adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                }
7953adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            }
7963adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            // Reject the media.
7973adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            Media reply = answer.newMedia(
7983adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    media.getType(), 0, 1, media.getProtocol());
7993adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            for (String format : media.getFormats()) {
8003adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                reply.setFormat(format, null);
8013adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            }
8023adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
8033adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        if (codec == null) {
8049329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            loge("createAnswer: no suitable codes");
8053adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            throw new IllegalStateException("Reject SDP: no suitable codecs");
8063adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
8079329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        if (DBG) log("createAnswer: answer=" + answer);
8083adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        return answer;
8093adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
8103adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
8113adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private SimpleSessionDescription createHoldOffer() {
8123adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        SimpleSessionDescription offer = createContinueOffer();
8133adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        offer.setAttribute("sendonly", "");
8149329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        if (DBG) log("createHoldOffer: offer=" + offer);
8153adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        return offer;
8163adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
8173adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
8183adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private SimpleSessionDescription createContinueOffer() {
8199329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        if (DBG) log("createContinueOffer");
8203adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        SimpleSessionDescription offer =
8213adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                new SimpleSessionDescription(mSessionId, getLocalIp());
8223adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        Media media = offer.newMedia(
8233adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
8243adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        AudioCodec codec = mAudioStream.getCodec();
8253adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        media.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
8263adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        int dtmfType = mAudioStream.getDtmfType();
8273adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        if (dtmfType != -1) {
8283adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            media.setRtpPayload(dtmfType, "telephone-event/8000", "0-15");
8293adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
8303adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        return offer;
8313adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
8323adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
8333adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private void grabWifiHighPerfLock() {
8343adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        if (mWifiHighPerfLock == null) {
8359329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            if (DBG) log("grabWifiHighPerfLock:");
8363adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            mWifiHighPerfLock = ((WifiManager)
8373adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    mContext.getSystemService(Context.WIFI_SERVICE))
8389329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                    .createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, LOG_TAG);
8393adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            mWifiHighPerfLock.acquire();
8403adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
8413adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
8423adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
8433adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private void releaseWifiHighPerfLock() {
8443adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        if (mWifiHighPerfLock != null) {
8459329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            if (DBG) log("releaseWifiHighPerfLock:");
8463adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            mWifiHighPerfLock.release();
8473adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            mWifiHighPerfLock = null;
8483adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
8493adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
8503adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
8513adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private boolean isWifiOn() {
8523adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        return (mWm.getConnectionInfo().getBSSID() == null) ? false : true;
8533adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
8543adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
8553adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    /** Toggles mute. */
8564e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan    public void toggleMute() {
8574e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        synchronized (this) {
85844afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan            mMuted = !mMuted;
85944afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan            setAudioGroupMode();
8603adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
8613adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
86298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
86398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang    /**
86498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * Checks if the call is muted.
86598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     *
86698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * @return true if the call is muted
86798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     */
8684e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan    public boolean isMuted() {
8694e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        synchronized (this) {
8704e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            return mMuted;
8714e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        }
8723adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
8733adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
874d3666076203ecc553211f8fa443ef259667ddd5eHung-ying Tyan    /**
875d3666076203ecc553211f8fa443ef259667ddd5eHung-ying Tyan     * Puts the device to speaker mode.
87616b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main     * <p class="note"><strong>Note:</strong> Requires the
87716b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main     *   {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS} permission.</p>
87844afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan     *
87944afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan     * @param speakerMode set true to enable speaker mode; false to disable
880d3666076203ecc553211f8fa443ef259667ddd5eHung-ying Tyan     */
8814e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan    public void setSpeakerMode(boolean speakerMode) {
8824e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        synchronized (this) {
8834e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
8844e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                    .setSpeakerphoneOn(speakerMode);
88544afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan            setAudioGroupMode();
8864e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        }
8873adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
88898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
88944afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan    private boolean isSpeakerOn() {
89044afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan        return ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
89144afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan                .isSpeakerphoneOn();
89244afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan    }
89344afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan
89498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang    /**
89516b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main     * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2883</a>,
89616b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main     * event 0--9 maps to decimal
8973adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * value 0--9, '*' to 10, '#' to 11, event 'A'--'D' to 12--15, and event
8983adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * flash to 16. Currently, event flash is not supported.
89998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     *
9003adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @param code the DTMF code to send. Value 0 to 15 (inclusive) are valid
9013adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     *        inputs.
90298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     */
9033adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    public void sendDtmf(int code) {
9043adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        sendDtmf(code, null);
9053adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
90698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
90798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang    /**
90816b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main     * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2883</a>,
90916b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main     * event 0--9 maps to decimal
9103adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * value 0--9, '*' to 10, '#' to 11, event 'A'--'D' to 12--15, and event
9113adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * flash to 16. Currently, event flash is not supported.
91298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     *
9133adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @param code the DTMF code to send. Value 0 to 15 (inclusive) are valid
9143adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     *        inputs.
91598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * @param result the result message to send when done
91698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     */
9174e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan    public void sendDtmf(int code, Message result) {
9184e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        synchronized (this) {
9194e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            AudioGroup audioGroup = getAudioGroup();
9204e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            if ((audioGroup != null) && (mSipSession != null)
9214e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                    && (SipSession.State.IN_CALL == getState())) {
9229329db04f13480ccdff013dcc00cdb96f12a921cWink Saville                if (DBG) log("sendDtmf: code=" + code + " result=" + result);
9234e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                audioGroup.sendDtmf(code);
9244e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            }
9254e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            if (result != null) result.sendToTarget();
9263adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
9273adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
92898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
92998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang    /**
93098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * Gets the {@link AudioStream} object used in this call. The object
93198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * represents the RTP stream that carries the audio data to and from the
93298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * peer. The object may not be created before the call is established. And
93398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * it is undefined after the call ends or the {@link #close} method is
93498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * called.
93598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     *
93698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * @return the {@link AudioStream} object or null if the RTP stream has not
93798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     *      yet been set up
9383adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @hide
93998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     */
9404e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan    public AudioStream getAudioStream() {
9414e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        synchronized (this) {
9424e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            return mAudioStream;
9434e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        }
9443adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
94598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
94698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang    /**
94798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * Gets the {@link AudioGroup} object which the {@link AudioStream} object
94898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * joins. The group object may not exist before the call is established.
94998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * Also, the {@code AudioStream} may change its group during a call (e.g.,
95098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * after the call is held/un-held). Finally, the {@code AudioGroup} object
95198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * returned by this method is undefined after the call ends or the
9520ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan     * {@link #close} method is called. If a group object is set by
9530ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan     * {@link #setAudioGroup(AudioGroup)}, then this method returns that object.
95498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     *
95598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * @return the {@link AudioGroup} object or null if the RTP stream has not
95698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     *      yet been set up
95798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     * @see #getAudioStream
9583adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @hide
95998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     */
9604e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan    public AudioGroup getAudioGroup() {
9614e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        synchronized (this) {
9624e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            if (mAudioGroup != null) return mAudioGroup;
9634e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            return ((mAudioStream == null) ? null : mAudioStream.getGroup());
9644e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        }
9653adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
96698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
96798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang    /**
9680ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan     * Sets the {@link AudioGroup} object which the {@link AudioStream} object
9690ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan     * joins. If {@code audioGroup} is null, then the {@code AudioGroup} object
97044afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan     * will be dynamically created when needed. Note that the mode of the
97144afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan     * {@code AudioGroup} is not changed according to the audio settings (i.e.,
97244afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan     * hold, mute, speaker phone) of this object. This is mainly used to merge
97344afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan     * multiple {@code SipAudioCall} objects to form a conference call. The
97444afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan     * settings of the first object (that merges others) override others'.
9750ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan     *
9760ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan     * @see #getAudioStream
9773adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * @hide
9780ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan     */
9794e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan    public void setAudioGroup(AudioGroup group) {
9804e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan        synchronized (this) {
9819329db04f13480ccdff013dcc00cdb96f12a921cWink Saville            if (DBG) log("setAudioGroup: group=" + group);
9824e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            if ((mAudioStream != null) && (mAudioStream.getGroup() != null)) {
9834e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan                mAudioStream.join(group);
9844e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            }
9854e22f5d6687c18440e2f38c7e242d66ad834d1d7Hung-ying Tyan            mAudioGroup = group;
9863adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
9873adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
9880ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan
9890ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan    /**
9903adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * Starts the audio for the established call. This method should be called
9913adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan     * after {@link Listener#onCallEstablished} is called.
99216b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main     * <p class="note"><strong>Note:</strong> Requires the
993d3666076203ecc553211f8fa443ef259667ddd5eHung-ying Tyan     *   {@link android.Manifest.permission#RECORD_AUDIO},
994d3666076203ecc553211f8fa443ef259667ddd5eHung-ying Tyan     *   {@link android.Manifest.permission#ACCESS_WIFI_STATE} and
99516b441b4ad92c6a5cbc6f27cb3705eaaaaee20c1Scott Main     *   {@link android.Manifest.permission#WAKE_LOCK} permissions.</p>
99698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang     */
9973adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    public void startAudio() {
9983adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        try {
9993adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            startAudioInternal();
10003adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        } catch (UnknownHostException e) {
10013adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            onError(SipErrorCode.PEER_NOT_REACHABLE, e.getMessage());
10023adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        } catch (Throwable e) {
10033adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            onError(SipErrorCode.CLIENT_ERROR, e.getMessage());
10043adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
10053adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
100698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
10073adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private synchronized void startAudioInternal() throws UnknownHostException {
10089329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        if (DBG) loge("startAudioInternal: mPeerSd=" + mPeerSd);
10093adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        if (mPeerSd == null) {
10103adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            throw new IllegalStateException("mPeerSd = null");
10113adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
101298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
10133adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        stopCall(DONT_RELEASE_SOCKET);
10143adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        mInCall = true;
101598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
10163adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        // Run exact the same logic in createAnswer() to setup mAudioStream.
10173adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        SimpleSessionDescription offer =
10183adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                new SimpleSessionDescription(mPeerSd);
10193adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        AudioStream stream = mAudioStream;
10203adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        AudioCodec codec = null;
10213adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        for (Media media : offer.getMedia()) {
10223adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            if ((codec == null) && (media.getPort() > 0)
10233adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    && "audio".equals(media.getType())
10243adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    && "RTP/AVP".equals(media.getProtocol())) {
10253adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                // Find the first audio codec we supported.
10263adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                for (int type : media.getRtpPayloadTypes()) {
10273adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    codec = AudioCodec.getCodec(
10283adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                            type, media.getRtpmap(type), media.getFmtp(type));
10293adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    if (codec != null) {
10303adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        break;
10313adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    }
10323adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                }
10333adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
10343adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                if (codec != null) {
10353adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    // Associate with the remote host.
10363adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    String address = media.getAddress();
10373adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    if (address == null) {
10383adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        address = offer.getAddress();
10393adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    }
10403adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    stream.associate(InetAddress.getByName(address),
10413adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                            media.getPort());
10423adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
10433adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    stream.setDtmfType(-1);
10443adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    stream.setCodec(codec);
10453adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    // Check if DTMF is supported in the same media.
10463adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    for (int type : media.getRtpPayloadTypes()) {
10473adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        String rtpmap = media.getRtpmap(type);
10483adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        if ((type != codec.type) && (rtpmap != null)
10493adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                                && rtpmap.startsWith("telephone-event")) {
10503adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                            stream.setDtmfType(type);
10513adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        }
10523adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    }
10533adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
10543adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    // Handle recvonly and sendonly.
10553adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    if (mHold) {
10563adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        stream.setMode(RtpStream.MODE_NORMAL);
10573adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    } else if (media.getAttribute("recvonly") != null) {
10583adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        stream.setMode(RtpStream.MODE_SEND_ONLY);
10593adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    } else if(media.getAttribute("sendonly") != null) {
10603adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        stream.setMode(RtpStream.MODE_RECEIVE_ONLY);
10613adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    } else if(offer.getAttribute("recvonly") != null) {
10623adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        stream.setMode(RtpStream.MODE_SEND_ONLY);
10633adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    } else if(offer.getAttribute("sendonly") != null) {
10643adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        stream.setMode(RtpStream.MODE_RECEIVE_ONLY);
10653adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    } else {
10663adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                        stream.setMode(RtpStream.MODE_NORMAL);
10673adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    }
10683adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                    break;
10693adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                }
10703adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            }
10713adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
10723adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        if (codec == null) {
10733adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            throw new IllegalStateException("Reject SDP: no suitable codecs");
10743adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
10753adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
10763adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        if (isWifiOn()) grabWifiHighPerfLock();
10773adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
10783adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        // AudioGroup logic:
10793adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        AudioGroup audioGroup = getAudioGroup();
10803adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        if (mHold) {
10813adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            // don't create an AudioGroup here; doing so will fail if
10823adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            // there's another AudioGroup out there that's active
10833adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        } else {
10843adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            if (audioGroup == null) audioGroup = new AudioGroup();
10853adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            stream.join(audioGroup);
108644afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan        }
108744afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan        setAudioGroupMode();
108844afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan    }
108944afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan
109044afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan    // set audio group mode based on current audio configuration
109144afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan    private void setAudioGroupMode() {
109244afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan        AudioGroup audioGroup = getAudioGroup();
10939329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        if (DBG) log("setAudioGroupMode: audioGroup=" + audioGroup);
109444afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan        if (audioGroup != null) {
109544afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan            if (mHold) {
109644afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan                audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
109744afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan            } else if (mMuted) {
10983adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                audioGroup.setMode(AudioGroup.MODE_MUTED);
109944afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan            } else if (isSpeakerOn()) {
110044afaba6d2381a83caf6d6a5f3b1982d47572f3cHung-ying Tyan                audioGroup.setMode(AudioGroup.MODE_ECHO_SUPPRESSION);
11013adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            } else {
11023adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                audioGroup.setMode(AudioGroup.MODE_NORMAL);
11033adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            }
11043adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
11053adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
11063adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
11073adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private void stopCall(boolean releaseSocket) {
11089329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        if (DBG) log("stopCall: releaseSocket=" + releaseSocket);
11093adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        releaseWifiHighPerfLock();
11103adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        if (mAudioStream != null) {
11113adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            mAudioStream.join(null);
11123adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
11133adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            if (releaseSocket) {
11143adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                mAudioStream.release();
11153adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan                mAudioStream = null;
11163adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            }
11173adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
11183adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
11193adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
11203adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private String getLocalIp() {
11213adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        return mSipSession.getLocalIp();
11223adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
112398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang
11243adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    private void throwSipException(Throwable throwable) throws SipException {
11253adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        if (throwable instanceof SipException) {
11263adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            throw (SipException) throwable;
11273adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        } else {
11283adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan            throw new SipException("", throwable);
11293adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan        }
11303adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
11313adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan
11329329db04f13480ccdff013dcc00cdb96f12a921cWink Saville    private void log(String s) {
11339329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        Rlog.d(LOG_TAG, s);
11349329db04f13480ccdff013dcc00cdb96f12a921cWink Saville    }
11359329db04f13480ccdff013dcc00cdb96f12a921cWink Saville
11369329db04f13480ccdff013dcc00cdb96f12a921cWink Saville    private void loge(String s) {
11379329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        Rlog.e(LOG_TAG, s);
11389329db04f13480ccdff013dcc00cdb96f12a921cWink Saville    }
11399329db04f13480ccdff013dcc00cdb96f12a921cWink Saville
11409329db04f13480ccdff013dcc00cdb96f12a921cWink Saville    private void loge(String s, Throwable t) {
11419329db04f13480ccdff013dcc00cdb96f12a921cWink Saville        Rlog.e(LOG_TAG, s, t);
11423adf1946e78b52686fa5458e24645b05da57dc22Hung-ying Tyan    }
114398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang}
1144