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