SipAudioCall.java revision f498e98d2e2910e866ec0728cebbe676dd475d9e
198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang/* 298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * Copyright (C) 2010 The Android Open Source Project 398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * 498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * Licensed under the Apache License, Version 2.0 (the "License"); 598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * you may not use this file except in compliance with the License. 698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * You may obtain a copy of the License at 798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * 898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * http://www.apache.org/licenses/LICENSE-2.0 998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * 1098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * Unless required by applicable law or agreed to in writing, software 1198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * distributed under the License is distributed on an "AS IS" BASIS, 1298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * See the License for the specific language governing permissions and 1498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * limitations under the License. 1598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang */ 1698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 1798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangpackage android.net.sip; 1898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 1998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport gov.nist.javax.sdp.fields.SDPKeywords; 2098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 2198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport android.content.Context; 2298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport android.media.AudioManager; 2398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport android.media.Ringtone; 2498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport android.media.RingtoneManager; 2598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport android.media.ToneGenerator; 2698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport android.net.Uri; 2798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport android.net.rtp.AudioCodec; 2898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport android.net.rtp.AudioGroup; 2998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport android.net.rtp.AudioStream; 3098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport android.net.rtp.RtpStream; 319248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wangimport android.net.wifi.WifiManager; 3298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport android.os.Message; 3398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport android.os.RemoteException; 3498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport android.os.Vibrator; 3598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport android.provider.Settings; 3698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport android.util.Log; 3798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 3898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport java.io.IOException; 3998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport java.net.InetAddress; 4098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport java.text.ParseException; 4198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport java.util.ArrayList; 4298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport java.util.HashMap; 4398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport java.util.List; 4498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport java.util.Map; 4598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport javax.sdp.SdpException; 4698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 4798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang/** 4898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * Class that handles an audio call over SIP. 4998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang */ 5098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang/** @hide */ 5198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangpublic class SipAudioCallImpl extends SipSessionAdapter 5298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang implements SipAudioCall { 5398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private static final String TAG = SipAudioCallImpl.class.getSimpleName(); 5498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private static final boolean RELEASE_SOCKET = true; 5598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private static final boolean DONT_RELEASE_SOCKET = false; 5698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private static final String AUDIO = "audio"; 5798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private static final int DTMF = 101; 58f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan private static final int SESSION_TIMEOUT = 5; // in seconds 5998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 6098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private Context mContext; 6198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private SipProfile mLocalProfile; 6298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private SipAudioCall.Listener mListener; 6398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private ISipSession mSipSession; 6498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private SdpSessionDescription mPeerSd; 6598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 660ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan private AudioStream mAudioStream; 670ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan private AudioGroup mAudioGroup; 6898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private SdpSessionDescription.AudioCodec mCodec; 6998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private long mSessionId = -1L; // SDP session ID 7098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private boolean mInCall = false; 7198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private boolean mMuted = false; 7298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private boolean mHold = false; 7398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 7498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private boolean mRingbackToneEnabled = true; 7598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private boolean mRingtoneEnabled = true; 7698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private Ringtone mRingtone; 7798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private ToneGenerator mRingbackTone; 7898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 7998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private SipProfile mPendingCallRequest; 809248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang private WifiManager mWm; 819248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang private WifiManager.WifiLock mWifiHighPerfLock; 8298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 83188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan private SipErrorCode mErrorCode; 84188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan private String mErrorMessage; 85188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan 8698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public SipAudioCallImpl(Context context, SipProfile localProfile) { 8798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mContext = context; 8898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mLocalProfile = localProfile; 899248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang mWm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 9098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 9198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 9298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public void setListener(SipAudioCall.Listener listener) { 9398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang setListener(listener, false); 9498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 9598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 9698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public void setListener(SipAudioCall.Listener listener, 9798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang boolean callbackImmediately) { 9898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mListener = listener; 9998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 100188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan if ((listener == null) || !callbackImmediately) { 101188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan // do nothing 102188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan } else if (mErrorCode != null) { 103188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan listener.onError(this, mErrorCode, mErrorMessage); 104188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan } else if (mInCall) { 105188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan if (mHold) { 106188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan listener.onCallHeld(this); 107188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan } else { 108188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan listener.onCallEstablished(this); 109188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan } 110188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan } else { 111188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan SipSessionState state = getState(); 112188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan switch (state) { 113188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan case READY_TO_CALL: 114188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan listener.onReadyToCall(this); 115188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan break; 116188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan case INCOMING_CALL: 117188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan listener.onRinging(this, getPeerProfile(mSipSession)); 118188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan break; 119188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan case OUTGOING_CALL: 120188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan listener.onCalling(this); 121188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan break; 122188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan case OUTGOING_CALL_RING_BACK: 123188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan listener.onRingingBack(this); 124188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan break; 125188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan } 12698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 12798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (Throwable t) { 12898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.e(TAG, "setListener()", t); 12998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 13098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 13198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 13298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public synchronized boolean isInCall() { 13398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return mInCall; 13498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 13598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 13698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public synchronized boolean isOnHold() { 13798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return mHold; 13898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 13998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 14098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public void close() { 14198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang close(true); 14298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 14398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 14498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private synchronized void close(boolean closeRtp) { 14598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (closeRtp) stopCall(RELEASE_SOCKET); 14698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang stopRingbackTone(); 14798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang stopRinging(); 148f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan 14998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mInCall = false; 15098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mHold = false; 15198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mSessionId = -1L; 152188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan mErrorCode = null; 153188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan mErrorMessage = null; 154f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan 155f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan if (mSipSession != null) { 156f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan try { 157f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan mSipSession.setListener(null); 158f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan } catch (RemoteException e) { 159f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan // don't care 160f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan } 161f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan mSipSession = null; 162f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan } 16398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 16498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 16598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public synchronized SipProfile getLocalProfile() { 16698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return mLocalProfile; 16798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 16898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 16998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public synchronized SipProfile getPeerProfile() { 17098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 17198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return (mSipSession == null) ? null : mSipSession.getPeerProfile(); 17298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (RemoteException e) { 17398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return null; 17498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 17598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 17698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 17798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public synchronized SipSessionState getState() { 17898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (mSipSession == null) return SipSessionState.READY_TO_CALL; 17998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 18098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return Enum.valueOf(SipSessionState.class, mSipSession.getState()); 18198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (RemoteException e) { 18298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return SipSessionState.REMOTE_ERROR; 18398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 18498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 18598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 18698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 18798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public synchronized ISipSession getSipSession() { 18898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return mSipSession; 18998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 19098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 19198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang @Override 19298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public void onCalling(ISipSession session) { 19398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.d(TAG, "calling... " + session); 19498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Listener listener = mListener; 19598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (listener != null) { 19698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 19772e725a66e388eca197a1e3ee118bc6481a82fe4Hung-ying Tyan listener.onCalling(this); 19898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (Throwable t) { 19998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.e(TAG, "onCalling()", t); 20098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 20198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 20298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 20398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 20498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang @Override 20598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public void onRingingBack(ISipSession session) { 20698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.d(TAG, "sip call ringing back: " + session); 20798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (!mInCall) startRingbackTone(); 20898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Listener listener = mListener; 20998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (listener != null) { 21098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 21172e725a66e388eca197a1e3ee118bc6481a82fe4Hung-ying Tyan listener.onRingingBack(this); 21298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (Throwable t) { 21398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.e(TAG, "onRingingBack()", t); 21498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 21598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 21698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 21798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 21898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang @Override 21998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public synchronized void onRinging(ISipSession session, 2206d0ef774a2492b0996ded3a43c300c7f72a94897Chia-chi Yeh SipProfile peerProfile, String sessionDescription) { 22198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 22298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if ((mSipSession == null) || !mInCall 22398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang || !session.getCallId().equals(mSipSession.getCallId())) { 22498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang // should not happen 22598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang session.endCall(); 22698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return; 22798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 22898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 22998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang // session changing request 23098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 23198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mPeerSd = new SdpSessionDescription(sessionDescription); 232f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan answerCall(SESSION_TIMEOUT); 23398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (Throwable e) { 23498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.e(TAG, "onRinging()", e); 23598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang session.endCall(); 23698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 23798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (RemoteException e) { 23898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.e(TAG, "onRinging()", e); 23998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 24098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 24198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 2426d0ef774a2492b0996ded3a43c300c7f72a94897Chia-chi Yeh private synchronized void establishCall(String sessionDescription) { 24398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang stopRingbackTone(); 24498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang stopRinging(); 24598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 24698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang SdpSessionDescription sd = 24798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang new SdpSessionDescription(sessionDescription); 24898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.d(TAG, "sip call established: " + sd); 24998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang startCall(sd); 25098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mInCall = true; 25198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (SdpException e) { 25298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.e(TAG, "createSessionDescription()", e); 25398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 25498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 25598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 25698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang @Override 25798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public void onCallEstablished(ISipSession session, 2586d0ef774a2492b0996ded3a43c300c7f72a94897Chia-chi Yeh String sessionDescription) { 25998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang establishCall(sessionDescription); 26098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Listener listener = mListener; 26198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (listener != null) { 26298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 26398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (mHold) { 26472e725a66e388eca197a1e3ee118bc6481a82fe4Hung-ying Tyan listener.onCallHeld(this); 26598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } else { 26672e725a66e388eca197a1e3ee118bc6481a82fe4Hung-ying Tyan listener.onCallEstablished(this); 26798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 26898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (Throwable t) { 26998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.e(TAG, "onCallEstablished()", t); 27098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 27198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 27298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 27398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 27498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang @Override 27598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public void onCallEnded(ISipSession session) { 27698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.d(TAG, "sip call ended: " + session); 27798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang close(); 27898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Listener listener = mListener; 27998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (listener != null) { 28098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 28172e725a66e388eca197a1e3ee118bc6481a82fe4Hung-ying Tyan listener.onCallEnded(this); 28298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (Throwable t) { 28398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.e(TAG, "onCallEnded()", t); 28498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 28598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 28698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 28798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 28898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang @Override 28998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public void onCallBusy(ISipSession session) { 29098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.d(TAG, "sip call busy: " + session); 29198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang close(false); 29298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Listener listener = mListener; 29398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (listener != null) { 29498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 29572e725a66e388eca197a1e3ee118bc6481a82fe4Hung-ying Tyan listener.onCallBusy(this); 29698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (Throwable t) { 29798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.e(TAG, "onCallBusy()", t); 29898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 29998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 30098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 30198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 302188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan private SipErrorCode getErrorCode(String errorCode) { 303188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan return Enum.valueOf(SipErrorCode.class, errorCode); 304188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan } 305188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan 30698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang @Override 3072bd51a23644fa0d5a460a4a939e95d5d4e85b891Hung-ying Tyan public void onCallChangeFailed(ISipSession session, String errorCode, 3082bd51a23644fa0d5a460a4a939e95d5d4e85b891Hung-ying Tyan String message) { 30998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.d(TAG, "sip call change failed: " + message); 310188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan mErrorCode = getErrorCode(errorCode); 311188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan mErrorMessage = message; 31298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Listener listener = mListener; 31398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (listener != null) { 31498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 31572e725a66e388eca197a1e3ee118bc6481a82fe4Hung-ying Tyan listener.onError(this, mErrorCode, message); 31698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (Throwable t) { 31798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.e(TAG, "onCallBusy()", t); 31898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 31998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 32098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 32198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 32298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang @Override 32372e725a66e388eca197a1e3ee118bc6481a82fe4Hung-ying Tyan public void onError(ISipSession session, String errorCodeString, 32472e725a66e388eca197a1e3ee118bc6481a82fe4Hung-ying Tyan String message) { 32572e725a66e388eca197a1e3ee118bc6481a82fe4Hung-ying Tyan Log.d(TAG, "sip session error: " + errorCodeString + ": " + message); 32672e725a66e388eca197a1e3ee118bc6481a82fe4Hung-ying Tyan SipErrorCode errorCode = mErrorCode = getErrorCode(errorCodeString); 327188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan mErrorMessage = message; 32898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang synchronized (this) { 329188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan if ((mErrorCode == SipErrorCode.DATA_CONNECTION_LOST) 330188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan || !isInCall()) { 331188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan close(true); 332188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan } 33398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 33498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Listener listener = mListener; 33598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (listener != null) { 33698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 33772e725a66e388eca197a1e3ee118bc6481a82fe4Hung-ying Tyan listener.onError(this, errorCode, message); 33898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (Throwable t) { 33998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.e(TAG, "onError()", t); 34098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 34198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 34298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 34398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 34498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public synchronized void attachCall(ISipSession session, 3456d0ef774a2492b0996ded3a43c300c7f72a94897Chia-chi Yeh String sessionDescription) throws SipException { 34698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mSipSession = session; 34798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 3486d0ef774a2492b0996ded3a43c300c7f72a94897Chia-chi Yeh mPeerSd = new SdpSessionDescription(sessionDescription); 34998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang session.setListener(this); 350188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan 351188f70fdaf7d8cd20e9386051d06ca5a3ba0b683Hung-ying Tyan if (getState() == SipSessionState.INCOMING_CALL) startRinging(); 35298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (Throwable e) { 35398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.e(TAG, "attachCall()", e); 35498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang throwSipException(e); 35598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 35698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 35798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 35898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public synchronized void makeCall(SipProfile peerProfile, 359f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan SipManager sipManager, int timeout) throws SipException { 36098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 36198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mSipSession = sipManager.createSipSession(mLocalProfile, this); 36298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (mSipSession == null) { 36398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang throw new SipException( 36498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang "Failed to create SipSession; network available?"); 36598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 366f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan mSipSession.makeCall(peerProfile, createOfferSessionDescription(), 367f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan timeout); 36898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (Throwable e) { 36998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (e instanceof SipException) { 37098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang throw (SipException) e; 37198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } else { 37298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang throwSipException(e); 37398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 37498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 37598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 37698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 37798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public synchronized void endCall() throws SipException { 37898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 37998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang stopRinging(); 38098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang stopCall(true); 38192a48830c7e1468155db24aa604efbbbbef8afc7Hung-ying Tyan mInCall = false; 38292a48830c7e1468155db24aa604efbbbbef8afc7Hung-ying Tyan 38392a48830c7e1468155db24aa604efbbbbef8afc7Hung-ying Tyan // perform the above local ops first and then network op 38492a48830c7e1468155db24aa604efbbbbef8afc7Hung-ying Tyan if (mSipSession != null) mSipSession.endCall(); 38598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (Throwable e) { 38698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang throwSipException(e); 38798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 38898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 38998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 390f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan public synchronized void holdCall(int timeout) throws SipException { 39198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (mHold) return; 39298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 393f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan mSipSession.changeCall(createHoldSessionDescription(), timeout); 39498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mHold = true; 39598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (Throwable e) { 39698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang throwSipException(e); 39798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 39898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 39998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang AudioGroup audioGroup = getAudioGroup(); 40098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_ON_HOLD); 40198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 40298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 403f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan public synchronized void answerCall(int timeout) throws SipException { 40498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 40598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang stopRinging(); 406f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan mSipSession.answerCall(createAnswerSessionDescription(), timeout); 40798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (Throwable e) { 40898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.e(TAG, "answerCall()", e); 40998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang throwSipException(e); 41098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 41198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 41298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 413f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan public synchronized void continueCall(int timeout) throws SipException { 41498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (!mHold) return; 41598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 41698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mHold = false; 417f498e98d2e2910e866ec0728cebbe676dd475d9eHung-ying Tyan mSipSession.changeCall(createContinueSessionDescription(), timeout); 41898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (Throwable e) { 41998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang throwSipException(e); 42098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 42198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 42298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang AudioGroup audioGroup = getAudioGroup(); 42398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_NORMAL); 42498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 42598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 4266d0ef774a2492b0996ded3a43c300c7f72a94897Chia-chi Yeh private String createOfferSessionDescription() { 42798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang AudioCodec[] codecs = AudioCodec.getSystemSupportedCodecs(); 42898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return createSdpBuilder(true, convert(codecs)).build(); 42998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 43098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 4316d0ef774a2492b0996ded3a43c300c7f72a94897Chia-chi Yeh private String createAnswerSessionDescription() { 43298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 43398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang // choose an acceptable media from mPeerSd to answer 43498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang SdpSessionDescription.AudioCodec codec = getCodec(mPeerSd); 43598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang SdpSessionDescription.Builder sdpBuilder = 43698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang createSdpBuilder(false, codec); 43798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (mPeerSd.isSendOnly(AUDIO)) { 43898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang sdpBuilder.addMediaAttribute(AUDIO, "recvonly", (String) null); 43998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } else if (mPeerSd.isReceiveOnly(AUDIO)) { 44098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang sdpBuilder.addMediaAttribute(AUDIO, "sendonly", (String) null); 44198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 44298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return sdpBuilder.build(); 44398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (SdpException e) { 44498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang throw new RuntimeException(e); 44598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 44698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 44798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 4486d0ef774a2492b0996ded3a43c300c7f72a94897Chia-chi Yeh private String createHoldSessionDescription() { 44998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 45098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return createSdpBuilder(false, mCodec) 45198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang .addMediaAttribute(AUDIO, "sendonly", (String) null) 45298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang .build(); 45398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (SdpException e) { 45498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang throw new RuntimeException(e); 45598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 45698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 45798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 4589248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang private void grabWifiHighPerfLock() { 4599248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang if (mWifiHighPerfLock == null) { 4609248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang Log.v(TAG, "acquire wifi high perf lock"); 4619248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang mWifiHighPerfLock = ((WifiManager) 4629248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang mContext.getSystemService(Context.WIFI_SERVICE)) 4639248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang .createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, TAG); 4649248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang mWifiHighPerfLock.acquire(); 4659248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang } 4669248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang } 4679248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang 4689248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang private void releaseWifiHighPerfLock() { 4699248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang if (mWifiHighPerfLock != null) { 4709248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang Log.v(TAG, "release wifi high perf lock"); 4719248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang mWifiHighPerfLock.release(); 4729248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang mWifiHighPerfLock = null; 4739248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang } 4749248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang } 4759248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang 4769248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang private boolean isWifiOn() { 4779248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang return (mWm.getConnectionInfo().getBSSID() == null) ? false : true; 4789248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang } 4799248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang 4806d0ef774a2492b0996ded3a43c300c7f72a94897Chia-chi Yeh private String createContinueSessionDescription() { 48198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return createSdpBuilder(true, mCodec).build(); 48298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 48398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 48498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private String getMediaDescription(SdpSessionDescription.AudioCodec codec) { 48598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return String.format("%d %s/%d", codec.payloadType, codec.name, 48698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang codec.sampleRate); 48798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 48898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 48998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private long getSessionId() { 49098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (mSessionId < 0) { 49198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mSessionId = System.currentTimeMillis(); 49298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 49398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return mSessionId; 49498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 49598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 49698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private SdpSessionDescription.Builder createSdpBuilder( 49798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang boolean addTelephoneEvent, 49898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang SdpSessionDescription.AudioCodec... codecs) { 49998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang String localIp = getLocalIp(); 50098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang SdpSessionDescription.Builder sdpBuilder; 50198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 50298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang long sessionVersion = System.currentTimeMillis(); 50398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang sdpBuilder = new SdpSessionDescription.Builder("SIP Call") 50498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang .setOrigin(mLocalProfile, getSessionId(), sessionVersion, 50598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang SDPKeywords.IN, SDPKeywords.IPV4, localIp) 50698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang .setConnectionInfo(SDPKeywords.IN, SDPKeywords.IPV4, 50798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang localIp); 50898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang List<Integer> codecIds = new ArrayList<Integer>(); 50998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang for (SdpSessionDescription.AudioCodec codec : codecs) { 51098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang codecIds.add(codec.payloadType); 51198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 51298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (addTelephoneEvent) codecIds.add(DTMF); 51398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang sdpBuilder.addMedia(AUDIO, getLocalMediaPort(), 1, "RTP/AVP", 51498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang codecIds.toArray(new Integer[codecIds.size()])); 51598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang for (SdpSessionDescription.AudioCodec codec : codecs) { 51698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang sdpBuilder.addMediaAttribute(AUDIO, "rtpmap", 51798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang getMediaDescription(codec)); 51898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 51998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (addTelephoneEvent) { 52098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang sdpBuilder.addMediaAttribute(AUDIO, "rtpmap", 52198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang DTMF + " telephone-event/8000"); 52298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 52398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang // FIXME: deal with vbr codec 52498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang sdpBuilder.addMediaAttribute(AUDIO, "ptime", "20"); 52598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (SdpException e) { 52698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang throw new RuntimeException(e); 52798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 52898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return sdpBuilder; 52998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 53098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 53198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public synchronized void toggleMute() { 53298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang AudioGroup audioGroup = getAudioGroup(); 53398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (audioGroup != null) { 53498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang audioGroup.setMode( 53598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mMuted ? AudioGroup.MODE_NORMAL : AudioGroup.MODE_MUTED); 53698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mMuted = !mMuted; 53798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 53898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 53998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 54098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public synchronized boolean isMuted() { 54198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return mMuted; 54298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 54398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 544b8298544a5e3f292fed1fef8cbb578789f3577ccChung-yih Wang public synchronized void setSpeakerMode(boolean speakerMode) { 54598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)) 546b8298544a5e3f292fed1fef8cbb578789f3577ccChung-yih Wang .setSpeakerphoneOn(speakerMode); 54798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 54898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 54998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public void sendDtmf(int code) { 55098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang sendDtmf(code, null); 55198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 55298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 55398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public synchronized void sendDtmf(int code, Message result) { 55498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang AudioGroup audioGroup = getAudioGroup(); 55598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if ((audioGroup != null) && (mSipSession != null) 55698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang && (SipSessionState.IN_CALL == getState())) { 55798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.v(TAG, "send DTMF: " + code); 55898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang audioGroup.sendDtmf(code); 55998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 56098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (result != null) result.sendToTarget(); 56198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 56298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 56398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public synchronized AudioStream getAudioStream() { 5640ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan return mAudioStream; 56598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 56698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 56798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public synchronized AudioGroup getAudioGroup() { 5680ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan if (mAudioGroup != null) return mAudioGroup; 5690ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan return ((mAudioStream == null) ? null : mAudioStream.getAudioGroup()); 5700ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan } 5710ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan 5720ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan public synchronized void setAudioGroup(AudioGroup group) { 5730ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan if ((mAudioStream != null) && (mAudioStream.getAudioGroup() != null)) { 5740ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan mAudioStream.join(group); 5750ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan } 5760ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan mAudioGroup = group; 57798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 57898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 57998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private SdpSessionDescription.AudioCodec getCodec(SdpSessionDescription sd) { 58098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang HashMap<String, AudioCodec> acceptableCodecs = 58198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang new HashMap<String, AudioCodec>(); 58298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang for (AudioCodec codec : AudioCodec.getSystemSupportedCodecs()) { 58398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang acceptableCodecs.put(codec.name, codec); 58498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 58598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang for (SdpSessionDescription.AudioCodec codec : sd.getAudioCodecs()) { 58698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang AudioCodec matchedCodec = acceptableCodecs.get(codec.name); 58798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (matchedCodec != null) return codec; 58898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 58998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.w(TAG, "no common codec is found, use PCM/0"); 59098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return convert(AudioCodec.ULAW); 59198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 59298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 59398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private AudioCodec convert(SdpSessionDescription.AudioCodec codec) { 59498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang AudioCodec c = AudioCodec.getSystemSupportedCodec(codec.name); 59598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return ((c == null) ? AudioCodec.ULAW : c); 59698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 59798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 59898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private SdpSessionDescription.AudioCodec convert(AudioCodec codec) { 59998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return new SdpSessionDescription.AudioCodec(codec.defaultType, 60098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang codec.name, codec.sampleRate, codec.sampleCount); 60198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 60298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 60398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private SdpSessionDescription.AudioCodec[] convert(AudioCodec[] codecs) { 60498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang SdpSessionDescription.AudioCodec[] copies = 60598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang new SdpSessionDescription.AudioCodec[codecs.length]; 60698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang for (int i = 0, len = codecs.length; i < len; i++) { 60798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang copies[i] = convert(codecs[i]); 60898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 60998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return copies; 61098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 61198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 61298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private void startCall(SdpSessionDescription peerSd) { 61398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang stopCall(DONT_RELEASE_SOCKET); 6149248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang if (isWifiOn()) grabWifiHighPerfLock(); 61598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mPeerSd = peerSd; 61698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang String peerMediaAddress = peerSd.getPeerMediaAddress(AUDIO); 61798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang // TODO: handle multiple media fields 61898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang int peerMediaPort = peerSd.getPeerMediaPort(AUDIO); 61998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.i(TAG, "start audiocall " + peerMediaAddress + ":" + peerMediaPort); 62098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 62198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang int localPort = getLocalMediaPort(); 62298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang int sampleRate = 8000; 62398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang int frameSize = sampleRate / 50; // 160 62498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 62598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang // TODO: get sample rate from sdp 62698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mCodec = getCodec(peerSd); 62798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 6280ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan AudioStream audioStream = mAudioStream; 62998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang audioStream.associate(InetAddress.getByName(peerMediaAddress), 63098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang peerMediaPort); 63198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang audioStream.setCodec(convert(mCodec), mCodec.payloadType); 63298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang audioStream.setDtmfType(DTMF); 63398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.d(TAG, "start media: localPort=" + localPort + ", peer=" 63498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang + peerMediaAddress + ":" + peerMediaPort); 63598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 63698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang audioStream.setMode(RtpStream.MODE_NORMAL); 63798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (!mHold) { 63898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang // FIXME: won't work if peer is not sending nor receiving 63998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (!peerSd.isSending(AUDIO)) { 64098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.d(TAG, " not receiving"); 64198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang audioStream.setMode(RtpStream.MODE_SEND_ONLY); 64298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 64398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (!peerSd.isReceiving(AUDIO)) { 64498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.d(TAG, " not sending"); 64598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang audioStream.setMode(RtpStream.MODE_RECEIVE_ONLY); 64698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 6470ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan 648b8298544a5e3f292fed1fef8cbb578789f3577ccChung-yih Wang /* The recorder volume will be very low if the device is in 649b8298544a5e3f292fed1fef8cbb578789f3577ccChung-yih Wang * IN_CALL mode. Therefore, we have to set the mode to NORMAL 650b8298544a5e3f292fed1fef8cbb578789f3577ccChung-yih Wang * in order to have the normal microphone level. 651b8298544a5e3f292fed1fef8cbb578789f3577ccChung-yih Wang */ 652b8298544a5e3f292fed1fef8cbb578789f3577ccChung-yih Wang ((AudioManager) mContext.getSystemService 653b8298544a5e3f292fed1fef8cbb578789f3577ccChung-yih Wang (Context.AUDIO_SERVICE)) 654b8298544a5e3f292fed1fef8cbb578789f3577ccChung-yih Wang .setMode(AudioManager.MODE_NORMAL); 65598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 65698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 6570ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan // AudioGroup logic: 6580ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan AudioGroup audioGroup = getAudioGroup(); 65998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (mHold) { 6600ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan if (audioGroup != null) { 6610ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan audioGroup.setMode(AudioGroup.MODE_ON_HOLD); 6620ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan } 6630ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan // don't create an AudioGroup here; doing so will fail if 6640ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan // there's another AudioGroup out there that's active 66598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } else { 6660ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan if (audioGroup == null) audioGroup = new AudioGroup(); 6670ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan audioStream.join(audioGroup); 6680ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan if (mMuted) { 6690ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan audioGroup.setMode(AudioGroup.MODE_MUTED); 6700ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan } else { 6710ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan audioGroup.setMode(AudioGroup.MODE_NORMAL); 6720ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan } 67398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 67498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (Exception e) { 67598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.e(TAG, "call()", e); 67698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 67798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 67898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 67998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private void stopCall(boolean releaseSocket) { 68098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.d(TAG, "stop audiocall"); 6819248b2a99ffa82713a8a6d71d0b68667b8f187e7Chung-yih Wang releaseWifiHighPerfLock(); 6820ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan if (mAudioStream != null) { 6830ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan mAudioStream.join(null); 68498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 68598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (releaseSocket) { 6860ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan mAudioStream.release(); 6870ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan mAudioStream = null; 68898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 68998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 69098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 69198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 69298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private int getLocalMediaPort() { 6930ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan if (mAudioStream != null) return mAudioStream.getLocalPort(); 69498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 6950ca131a2c0474ace43678a130eeee0d491a5fac4Hung-ying Tyan AudioStream s = mAudioStream = 69698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang new AudioStream(InetAddress.getByName(getLocalIp())); 69798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return s.getLocalPort(); 69898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (IOException e) { 69998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Log.w(TAG, "getLocalMediaPort(): " + e); 70098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang throw new RuntimeException(e); 70198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 70298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 70398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 70498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private String getLocalIp() { 70598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 70698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return mSipSession.getLocalIp(); 70798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (RemoteException e) { 70898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang // FIXME 70998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return "127.0.0.1"; 71098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 71198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 71298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 71398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public synchronized void setRingbackToneEnabled(boolean enabled) { 71498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mRingbackToneEnabled = enabled; 71598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 71698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 71798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public synchronized void setRingtoneEnabled(boolean enabled) { 71898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mRingtoneEnabled = enabled; 71998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 72098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 72198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private void startRingbackTone() { 72298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (!mRingbackToneEnabled) return; 72398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (mRingbackTone == null) { 72498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang // The volume relative to other sounds in the stream 72598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang int toneVolume = 80; 72698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mRingbackTone = new ToneGenerator( 72798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang AudioManager.STREAM_VOICE_CALL, toneVolume); 72898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 72998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mRingbackTone.startTone(ToneGenerator.TONE_CDMA_LOW_PBX_L); 73098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 73198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 73298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private void stopRingbackTone() { 73398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (mRingbackTone != null) { 73498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mRingbackTone.stopTone(); 73598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mRingbackTone.release(); 73698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mRingbackTone = null; 73798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 73898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 73998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 74098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private void startRinging() { 74198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (!mRingtoneEnabled) return; 74298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang ((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE)) 74398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang .vibrate(new long[] {0, 1000, 1000}, 1); 74498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang AudioManager am = (AudioManager) 74598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mContext.getSystemService(Context.AUDIO_SERVICE); 74698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (am.getStreamVolume(AudioManager.STREAM_RING) > 0) { 74798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang String ringtoneUri = 74898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Settings.System.DEFAULT_RINGTONE_URI.toString(); 74998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mRingtone = RingtoneManager.getRingtone(mContext, 75098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang Uri.parse(ringtoneUri)); 75198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang mRingtone.play(); 75298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 75398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 75498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 75598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private void stopRinging() { 75698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang ((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE)) 75798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang .cancel(); 75898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (mRingtone != null) mRingtone.stop(); 75998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 76098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 76198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private void throwSipException(Throwable throwable) throws SipException { 76298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang if (throwable instanceof SipException) { 76398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang throw (SipException) throwable; 76498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } else { 76598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang throw new SipException("", throwable); 76698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 76798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 76898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 76998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private SipProfile getPeerProfile(ISipSession session) { 77098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang try { 77198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return session.getPeerProfile(); 77298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } catch (RemoteException e) { 77398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return null; 77498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 77598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 77698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang} 777