Refactoring SIP classes to get ready for API review.

+ replace SipAudioCall and its Listener interfaces with real implementations,
  + remove SipAudioCallImpl.java, most of it is has become part of SipAudioCall,
+ add SipSession and its Listener classes to wrap ISipSession and ISipSessionListener,
+ move SipSessionState to SipSession.State,
+ make SipManager keep context and remove the context argument from many methods of its,
+ rename SipManager.getInstance() to newInstance(),
+ rename constant names for action strings and extra keys to follow conventions,
+ set thread names for debugging purpose.

Change-Id: Ie1790dc0e8f49c06c7fc80d33fec0f673a9c3044
This commit is contained in:
Hung-ying Tyan
2010-09-16 04:11:32 +08:00
parent 524a6d8e9b
commit 84a357bb6a
10 changed files with 1535 additions and 1136 deletions

View File

@ -30,8 +30,8 @@ import android.net.sip.ISipSessionListener;
import android.net.sip.SipErrorCode;
import android.net.sip.SipManager;
import android.net.sip.SipProfile;
import android.net.sip.SipSession;
import android.net.sip.SipSessionAdapter;
import android.net.sip.SipSessionState;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Bundle;
@ -143,7 +143,7 @@ public final class SipService extends ISipService.Stub {
}
private void openToReceiveCalls(SipProfile localProfile) {
open3(localProfile, SipManager.SIP_INCOMING_CALL_ACTION, null);
open3(localProfile, SipManager.ACTION_SIP_INCOMING_CALL, null);
}
public synchronized void open3(SipProfile localProfile,
@ -255,15 +255,15 @@ public final class SipService extends ISipService.Stub {
private void notifyProfileAdded(SipProfile localProfile) {
if (DEBUG) Log.d(TAG, "notify: profile added: " + localProfile);
Intent intent = new Intent(SipManager.SIP_ADD_PHONE_ACTION);
intent.putExtra(SipManager.LOCAL_URI_KEY, localProfile.getUriString());
Intent intent = new Intent(SipManager.ACTION_SIP_ADD_PHONE);
intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
mContext.sendBroadcast(intent);
}
private void notifyProfileRemoved(SipProfile localProfile) {
if (DEBUG) Log.d(TAG, "notify: profile removed: " + localProfile);
Intent intent = new Intent(SipManager.SIP_REMOVE_PHONE_ACTION);
intent.putExtra(SipManager.LOCAL_URI_KEY, localProfile.getUriString());
Intent intent = new Intent(SipManager.ACTION_SIP_REMOVE_PHONE);
intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
mContext.sendBroadcast(intent);
}
@ -474,8 +474,8 @@ public final class SipService extends ISipService.Stub {
// send out incoming call broadcast
addPendingSession(session);
Intent intent = SipManager.createIncomingCallBroadcast(
mIncomingCallBroadcastAction, session.getCallId(),
sessionDescription);
session.getCallId(), sessionDescription)
.setAction(mIncomingCallBroadcastAction);
if (DEBUG) Log.d(TAG, " ringing~~ " + getUri() + ": "
+ caller.getUri() + ": " + session.getCallId()
+ " " + mIncomingCallBroadcastAction);
@ -613,10 +613,10 @@ public final class SipService extends ISipService.Stub {
try {
int state = (mSession == null)
? SipSessionState.READY_TO_CALL
? SipSession.State.READY_TO_CALL
: mSession.getState();
if ((state == SipSessionState.REGISTERING)
|| (state == SipSessionState.DEREGISTERING)) {
if ((state == SipSession.State.REGISTERING)
|| (state == SipSession.State.DEREGISTERING)) {
mProxy.onRegistering(mSession);
} else if (mRegistered) {
int duration = (int)
@ -1138,7 +1138,8 @@ public final class SipService extends ISipService.Stub {
event.mTriggerTime += event.mPeriod;
// run the callback in a new thread to prevent deadlock
new Thread(event.mCallback).start();
new Thread(event.mCallback, "SipServiceTimerCallbackThread")
.start();
}
if (DEBUG_TIMER) {
Log.d(TAG, "after timeout execution");

View File

@ -28,8 +28,8 @@ import android.net.sip.ISipSessionListener;
import android.net.sip.SessionDescription;
import android.net.sip.SipErrorCode;
import android.net.sip.SipProfile;
import android.net.sip.SipSession;
import android.net.sip.SipSessionAdapter;
import android.net.sip.SipSessionState;
import android.text.TextUtils;
import android.util.Log;
@ -121,7 +121,7 @@ class SipSessionGroup implements SipListener {
reset(localIp);
}
void reset(String localIp) throws SipException, IOException {
synchronized void reset(String localIp) throws SipException, IOException {
mLocalIp = localIp;
if (localIp == null) return;
@ -301,7 +301,7 @@ class SipSessionGroup implements SipListener {
boolean processed = (session != null) && session.process(event);
if (isLoggable && processed) {
Log.d(TAG, "new state after: "
+ SipSessionState.toString(session.mState));
+ SipSession.State.toString(session.mState));
}
} catch (Throwable e) {
Log.w(TAG, "event process error: " + event, e);
@ -332,7 +332,7 @@ class SipSessionGroup implements SipListener {
public boolean process(EventObject evt) throws SipException {
if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~ " + this + ": "
+ SipSessionState.toString(mState) + ": processing "
+ SipSession.State.toString(mState) + ": processing "
+ log(evt));
if (isRequestEvent(Request.INVITE, evt)) {
RequestEvent event = (RequestEvent) evt;
@ -342,7 +342,7 @@ class SipSessionGroup implements SipListener {
newSession.mDialog = newSession.mServerTransaction.getDialog();
newSession.mInviteReceived = event;
newSession.mPeerProfile = createPeerProfile(event.getRequest());
newSession.mState = SipSessionState.INCOMING_CALL;
newSession.mState = SipSession.State.INCOMING_CALL;
newSession.mPeerSessionDescription =
extractContent(event.getRequest());
addSipSession(newSession);
@ -361,7 +361,7 @@ class SipSessionGroup implements SipListener {
class SipSessionImpl extends ISipSession.Stub {
SipProfile mPeerProfile;
SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
int mState = SipSessionState.READY_TO_CALL;
int mState = SipSession.State.READY_TO_CALL;
RequestEvent mInviteReceived;
Dialog mDialog;
ServerTransaction mServerTransaction;
@ -381,7 +381,7 @@ class SipSessionGroup implements SipListener {
sleep(timeout);
if (mRunning) timeout();
}
}).start();
}, "SipSessionTimerThread").start();
}
synchronized void cancel() {
@ -416,7 +416,7 @@ class SipSessionGroup implements SipListener {
mInCall = false;
removeSipSession(this);
mPeerProfile = null;
mState = SipSessionState.READY_TO_CALL;
mState = SipSession.State.READY_TO_CALL;
mInviteReceived = null;
mDialog = null;
mServerTransaction = null;
@ -473,7 +473,7 @@ class SipSessionGroup implements SipListener {
onError(e);
}
}
}).start();
}, "SipSessionAsyncCmdThread").start();
}
public void makeCall(SipProfile peerProfile, String sessionDescription,
@ -523,10 +523,10 @@ class SipSessionGroup implements SipListener {
}
public void sendKeepAlive() {
mState = SipSessionState.PINGING;
mState = SipSession.State.PINGING;
try {
processCommand(new OptionsCommand());
while (SipSessionState.PINGING == mState) {
while (SipSession.State.PINGING == mState) {
Thread.sleep(1000);
}
} catch (SipException e) {
@ -553,7 +553,7 @@ class SipSessionGroup implements SipListener {
try {
String s = super.toString();
return s.substring(s.indexOf("@")) + ":"
+ SipSessionState.toString(mState);
+ SipSession.State.toString(mState);
} catch (Throwable e) {
return super.toString();
}
@ -561,7 +561,7 @@ class SipSessionGroup implements SipListener {
public boolean process(EventObject evt) throws SipException {
if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~ " + this + ": "
+ SipSessionState.toString(mState) + ": processing "
+ SipSession.State.toString(mState) + ": processing "
+ log(evt));
synchronized (SipSessionGroup.this) {
if (isClosed()) return false;
@ -577,30 +577,30 @@ class SipSessionGroup implements SipListener {
boolean processed;
switch (mState) {
case SipSessionState.REGISTERING:
case SipSessionState.DEREGISTERING:
case SipSession.State.REGISTERING:
case SipSession.State.DEREGISTERING:
processed = registeringToReady(evt);
break;
case SipSessionState.PINGING:
case SipSession.State.PINGING:
processed = keepAliveProcess(evt);
break;
case SipSessionState.READY_TO_CALL:
case SipSession.State.READY_TO_CALL:
processed = readyForCall(evt);
break;
case SipSessionState.INCOMING_CALL:
case SipSession.State.INCOMING_CALL:
processed = incomingCall(evt);
break;
case SipSessionState.INCOMING_CALL_ANSWERING:
case SipSession.State.INCOMING_CALL_ANSWERING:
processed = incomingCallToInCall(evt);
break;
case SipSessionState.OUTGOING_CALL:
case SipSessionState.OUTGOING_CALL_RING_BACK:
case SipSession.State.OUTGOING_CALL:
case SipSession.State.OUTGOING_CALL_RING_BACK:
processed = outgoingCall(evt);
break;
case SipSessionState.OUTGOING_CALL_CANCELING:
case SipSession.State.OUTGOING_CALL_CANCELING:
processed = outgoingCallToReady(evt);
break;
case SipSessionState.IN_CALL:
case SipSession.State.IN_CALL:
processed = inCall(evt);
break;
default:
@ -650,8 +650,8 @@ class SipSessionGroup implements SipListener {
private void processTransactionTerminated(
TransactionTerminatedEvent event) {
switch (mState) {
case SipSessionState.IN_CALL:
case SipSessionState.READY_TO_CALL:
case SipSession.State.IN_CALL:
case SipSession.State.READY_TO_CALL:
Log.d(TAG, "Transaction terminated; do nothing");
break;
default:
@ -670,27 +670,27 @@ class SipSessionGroup implements SipListener {
? event.getServerTransaction()
: event.getClientTransaction();
if ((current != target) && (mState != SipSessionState.PINGING)) {
if ((current != target) && (mState != SipSession.State.PINGING)) {
Log.d(TAG, "not the current transaction; current=" + current
+ ", timed out=" + target);
return;
}
switch (mState) {
case SipSessionState.REGISTERING:
case SipSessionState.DEREGISTERING:
case SipSession.State.REGISTERING:
case SipSession.State.DEREGISTERING:
reset();
mProxy.onRegistrationTimeout(this);
break;
case SipSessionState.INCOMING_CALL:
case SipSessionState.INCOMING_CALL_ANSWERING:
case SipSessionState.OUTGOING_CALL:
case SipSessionState.OUTGOING_CALL_CANCELING:
case SipSession.State.INCOMING_CALL:
case SipSession.State.INCOMING_CALL_ANSWERING:
case SipSession.State.OUTGOING_CALL:
case SipSession.State.OUTGOING_CALL_CANCELING:
onError(SipErrorCode.TIME_OUT, event.toString());
break;
case SipSessionState.PINGING:
case SipSession.State.PINGING:
reset();
mReRegisterFlag = true;
mState = SipSessionState.READY_TO_CALL;
mState = SipSession.State.READY_TO_CALL;
break;
default:
@ -764,7 +764,7 @@ class SipSessionGroup implements SipListener {
switch (statusCode) {
case Response.OK:
int state = mState;
onRegistrationDone((state == SipSessionState.REGISTERING)
onRegistrationDone((state == SipSession.State.REGISTERING)
? getExpiryTime(((ResponseEvent) evt).getResponse())
: -1);
mLastNonce = null;
@ -851,7 +851,7 @@ class SipSessionGroup implements SipListener {
generateTag());
mDialog = mClientTransaction.getDialog();
addSipSession(this);
mState = SipSessionState.OUTGOING_CALL;
mState = SipSession.State.OUTGOING_CALL;
mProxy.onCalling(this);
startSessionTimer(cmd.getTimeout());
return true;
@ -861,7 +861,7 @@ class SipSessionGroup implements SipListener {
generateTag(), duration);
mDialog = mClientTransaction.getDialog();
addSipSession(this);
mState = SipSessionState.REGISTERING;
mState = SipSession.State.REGISTERING;
mProxy.onRegistering(this);
return true;
} else if (DEREGISTER == evt) {
@ -869,7 +869,7 @@ class SipSessionGroup implements SipListener {
generateTag(), 0);
mDialog = mClientTransaction.getDialog();
addSipSession(this);
mState = SipSessionState.DEREGISTERING;
mState = SipSession.State.DEREGISTERING;
mProxy.onRegistering(this);
return true;
}
@ -884,7 +884,7 @@ class SipSessionGroup implements SipListener {
mLocalProfile,
((MakeCallCommand) evt).getSessionDescription(),
mServerTransaction);
mState = SipSessionState.INCOMING_CALL_ANSWERING;
mState = SipSession.State.INCOMING_CALL_ANSWERING;
startSessionTimer(((MakeCallCommand) evt).getTimeout());
return true;
} else if (END_CALL == evt) {
@ -925,8 +925,8 @@ class SipSessionGroup implements SipListener {
int statusCode = response.getStatusCode();
switch (statusCode) {
case Response.RINGING:
if (mState == SipSessionState.OUTGOING_CALL) {
mState = SipSessionState.OUTGOING_CALL_RING_BACK;
if (mState == SipSession.State.OUTGOING_CALL) {
mState = SipSession.State.OUTGOING_CALL_RING_BACK;
mProxy.onRingingBack(this);
cancelSessionTimer();
}
@ -969,7 +969,7 @@ class SipSessionGroup implements SipListener {
// response comes back yet. We are cheating for not checking
// response.
mSipHelper.sendCancel(mClientTransaction);
mState = SipSessionState.OUTGOING_CALL_CANCELING;
mState = SipSession.State.OUTGOING_CALL_CANCELING;
startSessionTimer(CANCEL_CALL_TIMER);
return true;
}
@ -1025,7 +1025,7 @@ class SipSessionGroup implements SipListener {
} else if (isRequestEvent(Request.INVITE, evt)) {
// got Re-INVITE
RequestEvent event = mInviteReceived = (RequestEvent) evt;
mState = SipSessionState.INCOMING_CALL;
mState = SipSession.State.INCOMING_CALL;
mPeerSessionDescription = extractContent(event.getRequest());
mServerTransaction = null;
mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription);
@ -1038,7 +1038,7 @@ class SipSessionGroup implements SipListener {
// to change call
mClientTransaction = mSipHelper.sendReinvite(mDialog,
((MakeCallCommand) evt).getSessionDescription());
mState = SipSessionState.OUTGOING_CALL;
mState = SipSession.State.OUTGOING_CALL;
startSessionTimer(((MakeCallCommand) evt).getTimeout());
return true;
}
@ -1066,14 +1066,14 @@ class SipSessionGroup implements SipListener {
}
private void establishCall() {
mState = SipSessionState.IN_CALL;
mState = SipSession.State.IN_CALL;
mInCall = true;
cancelSessionTimer();
mProxy.onCallEstablished(this, mPeerSessionDescription);
}
private void fallbackToPreviousInCall(int errorCode, String message) {
mState = SipSessionState.IN_CALL;
mState = SipSession.State.IN_CALL;
mProxy.onCallChangeFailed(this, errorCode, message);
}
@ -1095,8 +1095,8 @@ class SipSessionGroup implements SipListener {
private void onError(int errorCode, String message) {
cancelSessionTimer();
switch (mState) {
case SipSessionState.REGISTERING:
case SipSessionState.DEREGISTERING:
case SipSession.State.REGISTERING:
case SipSession.State.DEREGISTERING:
onRegistrationFailed(errorCode, message);
break;
default:
@ -1270,7 +1270,7 @@ class SipSessionGroup implements SipListener {
private static boolean isLoggable(SipSessionImpl s) {
if (s != null) {
switch (s.mState) {
case SipSessionState.PINGING:
case SipSession.State.PINGING:
return DEBUG_PING;
}
}

View File

@ -40,7 +40,7 @@ class SipSessionListenerProxy extends ISipSessionListener.Stub {
// One thread for each calling back.
// Note: Guarantee ordering if the issue becomes important. Currently,
// the chance of handling two callback events at a time is none.
new Thread(runnable).start();
new Thread(runnable, "SipSessionCallbackThread").start();
}
public void onCalling(final ISipSession session) {

View File

@ -27,7 +27,7 @@ import android.net.sip.SipErrorCode;
import android.net.sip.SipException;
import android.net.sip.SipManager;
import android.net.sip.SipProfile;
import android.net.sip.SipSessionState;
import android.net.sip.SipSession;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Looper;
@ -92,7 +92,7 @@ public class SipPhone extends SipPhoneBase {
foregroundCall = new SipCall();
backgroundCall = new SipCall();
mProfile = profile;
mSipManager = SipManager.getInstance(context);
mSipManager = SipManager.newInstance(context);
// FIXME: what's this for SIP?
//Change the system property
@ -707,8 +707,8 @@ public class SipPhone extends SipPhoneBase {
void dial() throws SipException {
setState(Call.State.DIALING);
mSipAudioCall = mSipManager.makeAudioCall(mContext, mProfile,
mPeer, null, SESSION_TIMEOUT);
mSipAudioCall = mSipManager.makeAudioCall(mProfile, mPeer, null,
SESSION_TIMEOUT);
mSipAudioCall.setRingbackToneEnabled(false);
mSipAudioCall.setListener(mAdapter);
}
@ -808,20 +808,20 @@ public class SipPhone extends SipPhoneBase {
if (sipAudioCall.isOnHold()) return Call.State.HOLDING;
int sessionState = sipAudioCall.getState();
switch (sessionState) {
case SipSessionState.READY_TO_CALL: return Call.State.IDLE;
case SipSessionState.INCOMING_CALL:
case SipSessionState.INCOMING_CALL_ANSWERING: return Call.State.INCOMING;
case SipSessionState.OUTGOING_CALL: return Call.State.DIALING;
case SipSessionState.OUTGOING_CALL_RING_BACK: return Call.State.ALERTING;
case SipSessionState.OUTGOING_CALL_CANCELING: return Call.State.DISCONNECTING;
case SipSessionState.IN_CALL: return Call.State.ACTIVE;
case SipSession.State.READY_TO_CALL: return Call.State.IDLE;
case SipSession.State.INCOMING_CALL:
case SipSession.State.INCOMING_CALL_ANSWERING: return Call.State.INCOMING;
case SipSession.State.OUTGOING_CALL: return Call.State.DIALING;
case SipSession.State.OUTGOING_CALL_RING_BACK: return Call.State.ALERTING;
case SipSession.State.OUTGOING_CALL_CANCELING: return Call.State.DISCONNECTING;
case SipSession.State.IN_CALL: return Call.State.ACTIVE;
default:
Log.w(LOG_TAG, "illegal connection state: " + sessionState);
return Call.State.DISCONNECTED;
}
}
private abstract class SipAudioCallAdapter extends SipAudioCall.Adapter {
private abstract class SipAudioCallAdapter extends SipAudioCall.Listener {
protected abstract void onCallEnded(Connection.DisconnectCause cause);
protected abstract void onError(Connection.DisconnectCause cause);

File diff suppressed because it is too large Load Diff

View File

@ -1,766 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net.sip;
import android.content.Context;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.media.ToneGenerator;
import android.net.Uri;
import android.net.rtp.AudioCodec;
import android.net.rtp.AudioGroup;
import android.net.rtp.AudioStream;
import android.net.rtp.RtpStream;
import android.net.sip.SimpleSessionDescription.Media;
import android.net.wifi.WifiManager;
import android.os.Message;
import android.os.RemoteException;
import android.os.Vibrator;
import android.provider.Settings;
import android.util.Log;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Class that handles an audio call over SIP.
*/
/** @hide */
public class SipAudioCallImpl extends SipSessionAdapter
implements SipAudioCall {
private static final String TAG = SipAudioCallImpl.class.getSimpleName();
private static final boolean RELEASE_SOCKET = true;
private static final boolean DONT_RELEASE_SOCKET = false;
private static final int SESSION_TIMEOUT = 5; // in seconds
private Context mContext;
private SipProfile mLocalProfile;
private SipAudioCall.Listener mListener;
private ISipSession mSipSession;
private long mSessionId = System.currentTimeMillis();
private String mPeerSd;
private AudioStream mAudioStream;
private AudioGroup mAudioGroup;
private boolean mInCall = false;
private boolean mMuted = false;
private boolean mHold = false;
private boolean mRingbackToneEnabled = true;
private boolean mRingtoneEnabled = true;
private Ringtone mRingtone;
private ToneGenerator mRingbackTone;
private SipProfile mPendingCallRequest;
private WifiManager mWm;
private WifiManager.WifiLock mWifiHighPerfLock;
private int mErrorCode = SipErrorCode.NO_ERROR;
private String mErrorMessage;
public SipAudioCallImpl(Context context, SipProfile localProfile) {
mContext = context;
mLocalProfile = localProfile;
mWm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
}
public void setListener(SipAudioCall.Listener listener) {
setListener(listener, false);
}
public void setListener(SipAudioCall.Listener listener,
boolean callbackImmediately) {
mListener = listener;
try {
if ((listener == null) || !callbackImmediately) {
// do nothing
} else if (mErrorCode != SipErrorCode.NO_ERROR) {
listener.onError(this, mErrorCode, mErrorMessage);
} else if (mInCall) {
if (mHold) {
listener.onCallHeld(this);
} else {
listener.onCallEstablished(this);
}
} else {
int state = getState();
switch (state) {
case SipSessionState.READY_TO_CALL:
listener.onReadyToCall(this);
break;
case SipSessionState.INCOMING_CALL:
listener.onRinging(this, getPeerProfile(mSipSession));
break;
case SipSessionState.OUTGOING_CALL:
listener.onCalling(this);
break;
case SipSessionState.OUTGOING_CALL_RING_BACK:
listener.onRingingBack(this);
break;
}
}
} catch (Throwable t) {
Log.e(TAG, "setListener()", t);
}
}
public synchronized boolean isInCall() {
return mInCall;
}
public synchronized boolean isOnHold() {
return mHold;
}
public void close() {
close(true);
}
private synchronized void close(boolean closeRtp) {
if (closeRtp) stopCall(RELEASE_SOCKET);
stopRingbackTone();
stopRinging();
mInCall = false;
mHold = false;
mSessionId = System.currentTimeMillis();
mErrorCode = SipErrorCode.NO_ERROR;
mErrorMessage = null;
if (mSipSession != null) {
try {
mSipSession.setListener(null);
} catch (RemoteException e) {
// don't care
}
mSipSession = null;
}
}
public synchronized SipProfile getLocalProfile() {
return mLocalProfile;
}
public synchronized SipProfile getPeerProfile() {
try {
return (mSipSession == null) ? null : mSipSession.getPeerProfile();
} catch (RemoteException e) {
return null;
}
}
public synchronized int getState() {
if (mSipSession == null) return SipSessionState.READY_TO_CALL;
try {
return mSipSession.getState();
} catch (RemoteException e) {
return SipSessionState.REMOTE_ERROR;
}
}
public synchronized ISipSession getSipSession() {
return mSipSession;
}
@Override
public void onCalling(ISipSession session) {
Log.d(TAG, "calling... " + session);
Listener listener = mListener;
if (listener != null) {
try {
listener.onCalling(this);
} catch (Throwable t) {
Log.e(TAG, "onCalling()", t);
}
}
}
@Override
public void onRingingBack(ISipSession session) {
Log.d(TAG, "sip call ringing back: " + session);
if (!mInCall) startRingbackTone();
Listener listener = mListener;
if (listener != null) {
try {
listener.onRingingBack(this);
} catch (Throwable t) {
Log.e(TAG, "onRingingBack()", t);
}
}
}
@Override
public synchronized void onRinging(ISipSession session,
SipProfile peerProfile, String sessionDescription) {
try {
if ((mSipSession == null) || !mInCall
|| !session.getCallId().equals(mSipSession.getCallId())) {
// should not happen
session.endCall();
return;
}
// session changing request
try {
String answer = createAnswer(sessionDescription).encode();
mSipSession.answerCall(answer, SESSION_TIMEOUT);
} catch (Throwable e) {
Log.e(TAG, "onRinging()", e);
session.endCall();
}
} catch (RemoteException e) {
Log.e(TAG, "onRinging()", e);
}
}
@Override
public void onCallEstablished(ISipSession session,
String sessionDescription) {
stopRingbackTone();
stopRinging();
mPeerSd = sessionDescription;
Log.v(TAG, "onCallEstablished()" + mPeerSd);
Listener listener = mListener;
if (listener != null) {
try {
if (mHold) {
listener.onCallHeld(this);
} else {
listener.onCallEstablished(this);
}
} catch (Throwable t) {
Log.e(TAG, "onCallEstablished()", t);
}
}
}
@Override
public void onCallEnded(ISipSession session) {
Log.d(TAG, "sip call ended: " + session);
Listener listener = mListener;
if (listener != null) {
try {
listener.onCallEnded(this);
} catch (Throwable t) {
Log.e(TAG, "onCallEnded()", t);
}
}
close();
}
@Override
public void onCallBusy(ISipSession session) {
Log.d(TAG, "sip call busy: " + session);
Listener listener = mListener;
if (listener != null) {
try {
listener.onCallBusy(this);
} catch (Throwable t) {
Log.e(TAG, "onCallBusy()", t);
}
}
close(false);
}
@Override
public void onCallChangeFailed(ISipSession session, int errorCode,
String message) {
Log.d(TAG, "sip call change failed: " + message);
mErrorCode = errorCode;
mErrorMessage = message;
Listener listener = mListener;
if (listener != null) {
try {
listener.onError(this, mErrorCode, message);
} catch (Throwable t) {
Log.e(TAG, "onCallBusy()", t);
}
}
}
@Override
public void onError(ISipSession session, int errorCode, String message) {
Log.d(TAG, "sip session error: " + SipErrorCode.toString(errorCode)
+ ": " + message);
mErrorCode = errorCode;
mErrorMessage = message;
Listener listener = mListener;
if (listener != null) {
try {
listener.onError(this, errorCode, message);
} catch (Throwable t) {
Log.e(TAG, "onError()", t);
}
}
synchronized (this) {
if ((errorCode == SipErrorCode.DATA_CONNECTION_LOST)
|| !isInCall()) {
close(true);
}
}
}
public synchronized void attachCall(ISipSession session,
String sessionDescription) throws SipException {
mSipSession = session;
mPeerSd = sessionDescription;
Log.v(TAG, "attachCall()" + mPeerSd);
try {
session.setListener(this);
if (getState() == SipSessionState.INCOMING_CALL) startRinging();
} catch (Throwable e) {
Log.e(TAG, "attachCall()", e);
throwSipException(e);
}
}
public synchronized void makeCall(SipProfile peerProfile,
SipManager sipManager, int timeout) throws SipException {
try {
mSipSession = sipManager.createSipSession(mLocalProfile, this);
if (mSipSession == null) {
throw new SipException(
"Failed to create SipSession; network available?");
}
mAudioStream = new AudioStream(InetAddress.getByName(getLocalIp()));
mSipSession.makeCall(peerProfile, createOffer().encode(), timeout);
} catch (Throwable e) {
if (e instanceof SipException) {
throw (SipException) e;
} else {
throwSipException(e);
}
}
}
public synchronized void endCall() throws SipException {
try {
stopRinging();
stopCall(RELEASE_SOCKET);
mInCall = false;
// perform the above local ops first and then network op
if (mSipSession != null) mSipSession.endCall();
} catch (Throwable e) {
throwSipException(e);
}
}
public synchronized void answerCall(int timeout) throws SipException {
try {
stopRinging();
mAudioStream = new AudioStream(InetAddress.getByName(getLocalIp()));
mSipSession.answerCall(createAnswer(mPeerSd).encode(), timeout);
} catch (Throwable e) {
Log.e(TAG, "answerCall()", e);
throwSipException(e);
}
}
public synchronized void holdCall(int timeout) throws SipException {
if (mHold) return;
try {
mSipSession.changeCall(createHoldOffer().encode(), timeout);
} catch (Throwable e) {
throwSipException(e);
}
mHold = true;
AudioGroup audioGroup = getAudioGroup();
if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
}
public synchronized void continueCall(int timeout) throws SipException {
if (!mHold) return;
try {
mSipSession.changeCall(createContinueOffer().encode(), timeout);
} catch (Throwable e) {
throwSipException(e);
}
mHold = false;
AudioGroup audioGroup = getAudioGroup();
if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_NORMAL);
}
private SimpleSessionDescription createOffer() {
SimpleSessionDescription offer =
new SimpleSessionDescription(mSessionId, getLocalIp());
AudioCodec[] codecs = AudioCodec.getCodecs();
Media media = offer.newMedia(
"audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
for (AudioCodec codec : AudioCodec.getCodecs()) {
media.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
}
media.setRtpPayload(127, "telephone-event/8000", "0-15");
return offer;
}
private SimpleSessionDescription createAnswer(String offerSd) {
SimpleSessionDescription offer =
new SimpleSessionDescription(offerSd);
SimpleSessionDescription answer =
new SimpleSessionDescription(mSessionId, getLocalIp());
AudioCodec codec = null;
for (Media media : offer.getMedia()) {
if ((codec == null) && (media.getPort() > 0)
&& "audio".equals(media.getType())
&& "RTP/AVP".equals(media.getProtocol())) {
// Find the first audio codec we supported.
for (int type : media.getRtpPayloadTypes()) {
codec = AudioCodec.getCodec(type, media.getRtpmap(type),
media.getFmtp(type));
if (codec != null) {
break;
}
}
if (codec != null) {
Media reply = answer.newMedia(
"audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
reply.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
// Check if DTMF is supported in the same media.
for (int type : media.getRtpPayloadTypes()) {
String rtpmap = media.getRtpmap(type);
if ((type != codec.type) && (rtpmap != null)
&& rtpmap.startsWith("telephone-event")) {
reply.setRtpPayload(
type, rtpmap, media.getFmtp(type));
}
}
// Handle recvonly and sendonly.
if (media.getAttribute("recvonly") != null) {
answer.setAttribute("sendonly", "");
} else if(media.getAttribute("sendonly") != null) {
answer.setAttribute("recvonly", "");
} else if(offer.getAttribute("recvonly") != null) {
answer.setAttribute("sendonly", "");
} else if(offer.getAttribute("sendonly") != null) {
answer.setAttribute("recvonly", "");
}
continue;
}
}
// Reject the media.
Media reply = answer.newMedia(
media.getType(), 0, 1, media.getProtocol());
for (String format : media.getFormats()) {
reply.setFormat(format, null);
}
}
if (codec == null) {
throw new IllegalStateException("Reject SDP: no suitable codecs");
}
return answer;
}
private SimpleSessionDescription createHoldOffer() {
SimpleSessionDescription offer = createContinueOffer();
offer.setAttribute("sendonly", "");
return offer;
}
private SimpleSessionDescription createContinueOffer() {
SimpleSessionDescription offer =
new SimpleSessionDescription(mSessionId, getLocalIp());
Media media = offer.newMedia(
"audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
AudioCodec codec = mAudioStream.getCodec();
media.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
int dtmfType = mAudioStream.getDtmfType();
if (dtmfType != -1) {
media.setRtpPayload(dtmfType, "telephone-event/8000", "0-15");
}
return offer;
}
private void grabWifiHighPerfLock() {
if (mWifiHighPerfLock == null) {
Log.v(TAG, "acquire wifi high perf lock");
mWifiHighPerfLock = ((WifiManager)
mContext.getSystemService(Context.WIFI_SERVICE))
.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, TAG);
mWifiHighPerfLock.acquire();
}
}
private void releaseWifiHighPerfLock() {
if (mWifiHighPerfLock != null) {
Log.v(TAG, "release wifi high perf lock");
mWifiHighPerfLock.release();
mWifiHighPerfLock = null;
}
}
private boolean isWifiOn() {
return (mWm.getConnectionInfo().getBSSID() == null) ? false : true;
}
public synchronized void toggleMute() {
AudioGroup audioGroup = getAudioGroup();
if (audioGroup != null) {
audioGroup.setMode(
mMuted ? AudioGroup.MODE_NORMAL : AudioGroup.MODE_MUTED);
mMuted = !mMuted;
}
}
public synchronized boolean isMuted() {
return mMuted;
}
public synchronized void setSpeakerMode(boolean speakerMode) {
((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
.setSpeakerphoneOn(speakerMode);
}
public void sendDtmf(int code) {
sendDtmf(code, null);
}
public synchronized void sendDtmf(int code, Message result) {
AudioGroup audioGroup = getAudioGroup();
if ((audioGroup != null) && (mSipSession != null)
&& (SipSessionState.IN_CALL == getState())) {
Log.v(TAG, "send DTMF: " + code);
audioGroup.sendDtmf(code);
}
if (result != null) result.sendToTarget();
}
public synchronized AudioStream getAudioStream() {
return mAudioStream;
}
public synchronized AudioGroup getAudioGroup() {
if (mAudioGroup != null) return mAudioGroup;
return ((mAudioStream == null) ? null : mAudioStream.getGroup());
}
public synchronized void setAudioGroup(AudioGroup group) {
if ((mAudioStream != null) && (mAudioStream.getGroup() != null)) {
mAudioStream.join(group);
}
mAudioGroup = group;
}
public void startAudio() {
try {
startAudioInternal();
} catch (UnknownHostException e) {
onError(mSipSession, SipErrorCode.PEER_NOT_REACHABLE,
e.getMessage());
} catch (Throwable e) {
onError(mSipSession, SipErrorCode.CLIENT_ERROR,
e.getMessage());
}
}
private synchronized void startAudioInternal() throws UnknownHostException {
if (mPeerSd == null) {
Log.v(TAG, "startAudioInternal() mPeerSd = null");
throw new IllegalStateException("mPeerSd = null");
}
stopCall(DONT_RELEASE_SOCKET);
mInCall = true;
// Run exact the same logic in createAnswer() to setup mAudioStream.
SimpleSessionDescription offer =
new SimpleSessionDescription(mPeerSd);
AudioStream stream = mAudioStream;
AudioCodec codec = null;
for (Media media : offer.getMedia()) {
if ((codec == null) && (media.getPort() > 0)
&& "audio".equals(media.getType())
&& "RTP/AVP".equals(media.getProtocol())) {
// Find the first audio codec we supported.
for (int type : media.getRtpPayloadTypes()) {
codec = AudioCodec.getCodec(
type, media.getRtpmap(type), media.getFmtp(type));
if (codec != null) {
break;
}
}
if (codec != null) {
// Associate with the remote host.
String address = media.getAddress();
if (address == null) {
address = offer.getAddress();
}
stream.associate(InetAddress.getByName(address),
media.getPort());
stream.setDtmfType(-1);
stream.setCodec(codec);
// Check if DTMF is supported in the same media.
for (int type : media.getRtpPayloadTypes()) {
String rtpmap = media.getRtpmap(type);
if ((type != codec.type) && (rtpmap != null)
&& rtpmap.startsWith("telephone-event")) {
stream.setDtmfType(type);
}
}
// Handle recvonly and sendonly.
if (mHold) {
stream.setMode(RtpStream.MODE_NORMAL);
} else if (media.getAttribute("recvonly") != null) {
stream.setMode(RtpStream.MODE_SEND_ONLY);
} else if(media.getAttribute("sendonly") != null) {
stream.setMode(RtpStream.MODE_RECEIVE_ONLY);
} else if(offer.getAttribute("recvonly") != null) {
stream.setMode(RtpStream.MODE_SEND_ONLY);
} else if(offer.getAttribute("sendonly") != null) {
stream.setMode(RtpStream.MODE_RECEIVE_ONLY);
} else {
stream.setMode(RtpStream.MODE_NORMAL);
}
break;
}
}
}
if (codec == null) {
throw new IllegalStateException("Reject SDP: no suitable codecs");
}
if (isWifiOn()) grabWifiHighPerfLock();
if (!mHold) {
/* The recorder volume will be very low if the device is in
* IN_CALL mode. Therefore, we have to set the mode to NORMAL
* in order to have the normal microphone level.
*/
((AudioManager) mContext.getSystemService
(Context.AUDIO_SERVICE))
.setMode(AudioManager.MODE_NORMAL);
}
// AudioGroup logic:
AudioGroup audioGroup = getAudioGroup();
if (mHold) {
if (audioGroup != null) {
audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
}
// don't create an AudioGroup here; doing so will fail if
// there's another AudioGroup out there that's active
} else {
if (audioGroup == null) audioGroup = new AudioGroup();
mAudioStream.join(audioGroup);
if (mMuted) {
audioGroup.setMode(AudioGroup.MODE_MUTED);
} else {
audioGroup.setMode(AudioGroup.MODE_NORMAL);
}
}
}
private void stopCall(boolean releaseSocket) {
Log.d(TAG, "stop audiocall");
releaseWifiHighPerfLock();
if (mAudioStream != null) {
mAudioStream.join(null);
if (releaseSocket) {
mAudioStream.release();
mAudioStream = null;
}
}
}
private String getLocalIp() {
try {
return mSipSession.getLocalIp();
} catch (RemoteException e) {
throw new IllegalStateException(e);
}
}
public synchronized void setRingbackToneEnabled(boolean enabled) {
mRingbackToneEnabled = enabled;
}
public synchronized void setRingtoneEnabled(boolean enabled) {
mRingtoneEnabled = enabled;
}
private void startRingbackTone() {
if (!mRingbackToneEnabled) return;
if (mRingbackTone == null) {
// The volume relative to other sounds in the stream
int toneVolume = 80;
mRingbackTone = new ToneGenerator(
AudioManager.STREAM_VOICE_CALL, toneVolume);
}
mRingbackTone.startTone(ToneGenerator.TONE_CDMA_LOW_PBX_L);
}
private void stopRingbackTone() {
if (mRingbackTone != null) {
mRingbackTone.stopTone();
mRingbackTone.release();
mRingbackTone = null;
}
}
private void startRinging() {
if (!mRingtoneEnabled) return;
((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE))
.vibrate(new long[] {0, 1000, 1000}, 1);
AudioManager am = (AudioManager)
mContext.getSystemService(Context.AUDIO_SERVICE);
if (am.getStreamVolume(AudioManager.STREAM_RING) > 0) {
String ringtoneUri =
Settings.System.DEFAULT_RINGTONE_URI.toString();
mRingtone = RingtoneManager.getRingtone(mContext,
Uri.parse(ringtoneUri));
mRingtone.play();
}
}
private void stopRinging() {
((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE))
.cancel();
if (mRingtone != null) mRingtone.stop();
}
private void throwSipException(Throwable throwable) throws SipException {
if (throwable instanceof SipException) {
throw (SipException) throwable;
} else {
throw new SipException("", throwable);
}
}
private SipProfile getPeerProfile(ISipSession session) {
try {
return session.getPeerProfile();
} catch (RemoteException e) {
return null;
}
}
}

View File

@ -30,8 +30,9 @@ import java.text.ParseException;
* The class provides API for various SIP related tasks. Specifically, the API
* allows an application to:
* <ul>
* <li>register a {@link SipProfile} to have the background SIP service listen
* to incoming calls and broadcast them with registered command string. See
* <li>open a {@link SipProfile} to get ready for making outbound calls or have
* the background SIP service listen to incoming calls and broadcast them
* with registered command string. See
* {@link #open(SipProfile, String, SipRegistrationListener)},
* {@link #open(SipProfile)}, {@link #close}, {@link #isOpened} and
* {@link #isRegistered}. It also facilitates handling of the incoming call
@ -40,39 +41,59 @@ import java.text.ParseException;
* {@link #getOfferSessionDescription} and {@link #takeAudioCall}.</li>
* <li>make/take SIP-based audio calls. See
* {@link #makeAudioCall} and {@link #takeAudioCall}.</li>
* <li>register/unregister with a SIP service provider. See
* <li>register/unregister with a SIP service provider manually. See
* {@link #register} and {@link #unregister}.</li>
* <li>process SIP events directly with a {@link ISipSession} created by
* <li>process SIP events directly with a {@link SipSession} created by
* {@link #createSipSession}.</li>
* </ul>
* @hide
*/
public class SipManager {
/** @hide */
public static final String SIP_INCOMING_CALL_ACTION =
/**
* Action string for the incoming call intent for the Phone app.
* Internal use only.
* @hide
*/
public static final String ACTION_SIP_INCOMING_CALL =
"com.android.phone.SIP_INCOMING_CALL";
/** @hide */
public static final String SIP_ADD_PHONE_ACTION =
/**
* Action string for the add-phone intent.
* Internal use only.
* @hide
*/
public static final String ACTION_SIP_ADD_PHONE =
"com.android.phone.SIP_ADD_PHONE";
/** @hide */
public static final String SIP_REMOVE_PHONE_ACTION =
/**
* Action string for the remove-phone intent.
* Internal use only.
* @hide
*/
public static final String ACTION_SIP_REMOVE_PHONE =
"com.android.phone.SIP_REMOVE_PHONE";
/** @hide */
public static final String LOCAL_URI_KEY = "LOCAL SIPURI";
/**
* Part of the ACTION_SIP_ADD_PHONE and ACTION_SIP_REMOVE_PHONE intents.
* Internal use only.
* @hide
*/
public static final String EXTRA_LOCAL_URI = "android:localSipUri";
private static final String CALL_ID_KEY = "CallID";
private static final String OFFER_SD_KEY = "OfferSD";
/** Part of the incoming call intent. */
public static final String EXTRA_CALL_ID = "android:sipCallID";
/** Part of the incoming call intent. */
public static final String EXTRA_OFFER_SD = "android:sipOfferSD";
private ISipService mSipService;
private Context mContext;
/**
* Gets a manager instance. Returns null if SIP API is not supported.
* Creates a manager instance. Returns null if SIP API is not supported.
*
* @param context application context for checking if SIP API is supported
* @param context application context for creating the manager object
* @return the manager instance or null if SIP API is not supported
*/
public static SipManager getInstance(Context context) {
return (isApiSupported(context) ? new SipManager() : null);
public static SipManager newInstance(Context context) {
return (isApiSupported(context) ? new SipManager(context) : null);
}
/**
@ -80,7 +101,7 @@ public class SipManager {
*/
public static boolean isApiSupported(Context context) {
return true;
/*
/* TODO: uncomment this before ship
return context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SIP);
*/
@ -91,7 +112,7 @@ public class SipManager {
*/
public static boolean isVoipSupported(Context context) {
return true;
/*
/* TODO: uncomment this before ship
return context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SIP_VOIP) && isApiSupported(context);
*/
@ -105,23 +126,21 @@ public class SipManager {
com.android.internal.R.bool.config_sip_wifi_only);
}
private SipManager() {
private SipManager(Context context) {
mContext = context;
createSipService();
}
private void createSipService() {
if (mSipService != null) return;
IBinder b = ServiceManager.getService(Context.SIP_SERVICE);
mSipService = ISipService.Stub.asInterface(b);
}
/**
* Opens the profile for making calls and/or receiving calls. Subsequent
* SIP calls can be made through the default phone UI. The caller may also
* make subsequent calls through {@link #makeAudioCall}.
* If the receiving-call option is enabled in the profile, the SIP service
* will register the profile to the corresponding server periodically in
* order to receive calls from the server.
* Opens the profile for making calls. The caller may make subsequent calls
* through {@link #makeAudioCall}. If one also wants to receive calls on the
* profile, use {@link #open(SipProfile, String, SipRegistrationListener)}
* instead.
*
* @param localProfile the SIP profile to make calls from
* @throws SipException if the profile contains incorrect settings or
@ -136,12 +155,11 @@ public class SipManager {
}
/**
* Opens the profile for making calls and/or receiving calls. Subsequent
* SIP calls can be made through the default phone UI. The caller may also
* make subsequent calls through {@link #makeAudioCall}.
* If the receiving-call option is enabled in the profile, the SIP service
* will register the profile to the corresponding server periodically in
* order to receive calls from the server.
* Opens the profile for making calls and/or receiving calls. The caller may
* make subsequent calls through {@link #makeAudioCall}. If the
* auto-registration option is enabled in the profile, the SIP service
* will register the profile to the corresponding SIP provider periodically
* in order to receive calls from the provider.
*
* @param localProfile the SIP profile to receive incoming calls for
* @param incomingCallBroadcastAction the action to be broadcast when an
@ -195,7 +213,8 @@ public class SipManager {
}
/**
* Checks if the specified profile is enabled to receive calls.
* Checks if the specified profile is opened in the SIP service for
* making and/or receiving calls.
*
* @param localProfileUri the URI of the profile in question
* @return true if the profile is enabled to receive calls
@ -210,11 +229,16 @@ public class SipManager {
}
/**
* Checks if the specified profile is registered to the server for
* receiving calls.
* Checks if the SIP service has successfully registered the profile to the
* SIP provider (specified in the profile) for receiving calls. Returning
* true from this method also implies the profile is opened
* ({@link #isOpened}).
*
* @param localProfileUri the URI of the profile in question
* @return true if the profile is registered to the server
* @return true if the profile is registered to the SIP provider; false if
* the profile has not been opened in the SIP service or the SIP
* service has not yet successfully registered the profile to the SIP
* provider
* @throws SipException if calling the SIP service results in an error
*/
public boolean isRegistered(String localProfileUri) throws SipException {
@ -231,7 +255,6 @@ public class SipManager {
* {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
* will be called.
*
* @param context context to create a {@link SipAudioCall} object
* @param localProfile the SIP profile to make the call from
* @param peerProfile the SIP profile to make the call to
* @param listener to listen to the call events from {@link SipAudioCall};
@ -241,10 +264,10 @@ public class SipManager {
* @throws SipException if calling the SIP service results in an error
* @see SipAudioCall.Listener.onError
*/
public SipAudioCall makeAudioCall(Context context, SipProfile localProfile,
public SipAudioCall makeAudioCall(SipProfile localProfile,
SipProfile peerProfile, SipAudioCall.Listener listener, int timeout)
throws SipException {
SipAudioCall call = new SipAudioCallImpl(context, localProfile);
SipAudioCall call = new SipAudioCall(mContext, localProfile);
call.setListener(listener);
call.makeCall(peerProfile, this, timeout);
return call;
@ -257,7 +280,6 @@ public class SipManager {
* {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
* will be called.
*
* @param context context to create a {@link SipAudioCall} object
* @param localProfileUri URI of the SIP profile to make the call from
* @param peerProfileUri URI of the SIP profile to make the call to
* @param listener to listen to the call events from {@link SipAudioCall};
@ -267,11 +289,11 @@ public class SipManager {
* @throws SipException if calling the SIP service results in an error
* @see SipAudioCall.Listener.onError
*/
public SipAudioCall makeAudioCall(Context context, String localProfileUri,
public SipAudioCall makeAudioCall(String localProfileUri,
String peerProfileUri, SipAudioCall.Listener listener, int timeout)
throws SipException {
try {
return makeAudioCall(context,
return makeAudioCall(
new SipProfile.Builder(localProfileUri).build(),
new SipProfile.Builder(peerProfileUri).build(), listener,
timeout);
@ -281,15 +303,14 @@ public class SipManager {
}
/**
* The method calls {@code takeAudioCall(context, incomingCallIntent,
* The method calls {@code takeAudioCall(incomingCallIntent,
* listener, true}.
*
* @see #takeAudioCall(Context, Intent, SipAudioCall.Listener, boolean)
* @see #takeAudioCall(Intent, SipAudioCall.Listener, boolean)
*/
public SipAudioCall takeAudioCall(Context context,
Intent incomingCallIntent, SipAudioCall.Listener listener)
throws SipException {
return takeAudioCall(context, incomingCallIntent, listener, true);
public SipAudioCall takeAudioCall(Intent incomingCallIntent,
SipAudioCall.Listener listener) throws SipException {
return takeAudioCall(incomingCallIntent, listener, true);
}
/**
@ -298,16 +319,15 @@ public class SipManager {
* {@link SipAudioCall.Listener#onRinging}
* callback.
*
* @param context context to create a {@link SipAudioCall} object
* @param incomingCallIntent the incoming call broadcast intent
* @param listener to listen to the call events from {@link SipAudioCall};
* can be null
* @return a {@link SipAudioCall} object
* @throws SipException if calling the SIP service results in an error
*/
public SipAudioCall takeAudioCall(Context context,
Intent incomingCallIntent, SipAudioCall.Listener listener,
boolean ringtoneEnabled) throws SipException {
public SipAudioCall takeAudioCall(Intent incomingCallIntent,
SipAudioCall.Listener listener, boolean ringtoneEnabled)
throws SipException {
if (incomingCallIntent == null) return null;
String callId = getCallId(incomingCallIntent);
@ -324,10 +344,10 @@ public class SipManager {
try {
ISipSession session = mSipService.getPendingSession(callId);
if (session == null) return null;
SipAudioCall call = new SipAudioCallImpl(
context, session.getLocalProfile());
SipAudioCall call = new SipAudioCall(
mContext, session.getLocalProfile());
call.setRingtoneEnabled(ringtoneEnabled);
call.attachCall(session, offerSd);
call.attachCall(new SipSession(session), offerSd);
call.setListener(listener);
return call;
} catch (Throwable t) {
@ -355,7 +375,7 @@ public class SipManager {
* @return the call ID or null if the intent does not contain it
*/
public static String getCallId(Intent incomingCallIntent) {
return incomingCallIntent.getStringExtra(CALL_ID_KEY);
return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
}
/**
@ -367,30 +387,30 @@ public class SipManager {
* have it
*/
public static String getOfferSessionDescription(Intent incomingCallIntent) {
return incomingCallIntent.getStringExtra(OFFER_SD_KEY);
return incomingCallIntent.getStringExtra(EXTRA_OFFER_SD);
}
/**
* Creates an incoming call broadcast intent.
*
* @param action the action string to broadcast
* @param callId the call ID of the incoming call
* @param sessionDescription the session description of the incoming call
* @return the incoming call intent
* @hide
*/
public static Intent createIncomingCallBroadcast(String action,
String callId, String sessionDescription) {
Intent intent = new Intent(action);
intent.putExtra(CALL_ID_KEY, callId);
intent.putExtra(OFFER_SD_KEY, sessionDescription);
public static Intent createIncomingCallBroadcast(String callId,
String sessionDescription) {
Intent intent = new Intent();
intent.putExtra(EXTRA_CALL_ID, callId);
intent.putExtra(EXTRA_OFFER_SD, sessionDescription);
return intent;
}
/**
* Registers the profile to the corresponding server for receiving calls.
* {@link #open} is still needed to be called at least once in order for
* the SIP service to broadcast an intent when an incoming call is received.
* Manually registers the profile to the corresponding SIP provider for
* receiving calls. {@link #open(SipProfile, String, SipRegistrationListener)}
* is still needed to be called at least once in order for the SIP service
* to broadcast an intent when an incoming call is received.
*
* @param localProfile the SIP profile to register with
* @param expiryTime registration expiration time (in seconds)
@ -409,8 +429,10 @@ public class SipManager {
}
/**
* Unregisters the profile from the corresponding server for not receiving
* further calls.
* Manually unregisters the profile from the corresponding SIP provider for
* stop receiving further calls. This may interference with the auto
* registration process in the SIP service if the auto-registration option
* in the profile is enabled.
*
* @param localProfile the SIP profile to register with
* @param listener to listen to the registration events
@ -460,10 +482,11 @@ public class SipManager {
* @param localProfile the SIP profile the session is associated with
* @param listener to listen to SIP session events
*/
public ISipSession createSipSession(SipProfile localProfile,
ISipSessionListener listener) throws SipException {
public SipSession createSipSession(SipProfile localProfile,
SipSession.Listener listener) throws SipException {
try {
return mSipService.createSession(localProfile, listener);
ISipSession s = mSipService.createSession(localProfile, null);
return new SipSession(s, listener);
} catch (RemoteException e) {
throw new SipException("createSipSession()", e);
}

View File

@ -48,7 +48,6 @@ public class SipProfile implements Parcelable, Serializable, Cloneable {
private boolean mAutoRegistration = true;
private transient int mCallingUid = 0;
/** @hide */
public static final Parcelable.Creator<SipProfile> CREATOR =
new Parcelable.Creator<SipProfile>() {
public SipProfile createFromParcel(Parcel in) {
@ -287,7 +286,7 @@ public class SipProfile implements Parcelable, Serializable, Cloneable {
mCallingUid = in.readInt();
}
/** @hide */
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeSerializable(mAddress);
out.writeString(mProxyAddress);
@ -300,7 +299,7 @@ public class SipProfile implements Parcelable, Serializable, Cloneable {
out.writeInt(mCallingUid);
}
/** @hide */
@Override
public int describeContents() {
return 0;
}

View File

@ -0,0 +1,531 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net.sip;
import android.os.RemoteException;
import android.util.Log;
/**
* A SIP session that is associated with a SIP dialog or a standalone
* transaction not within a dialog.
* @hide
*/
public final class SipSession {
private static final String TAG = "SipSession";
/**
* Defines {@link SipSession} states.
* @hide
*/
public static class State {
/** When session is ready to initiate a call or transaction. */
public static final int READY_TO_CALL = 0;
/** When the registration request is sent out. */
public static final int REGISTERING = 1;
/** When the unregistration request is sent out. */
public static final int DEREGISTERING = 2;
/** When an INVITE request is received. */
public static final int INCOMING_CALL = 3;
/** When an OK response is sent for the INVITE request received. */
public static final int INCOMING_CALL_ANSWERING = 4;
/** When an INVITE request is sent. */
public static final int OUTGOING_CALL = 5;
/** When a RINGING response is received for the INVITE request sent. */
public static final int OUTGOING_CALL_RING_BACK = 6;
/** When a CANCEL request is sent for the INVITE request sent. */
public static final int OUTGOING_CALL_CANCELING = 7;
/** When a call is established. */
public static final int IN_CALL = 8;
/** When an OPTIONS request is sent. */
public static final int PINGING = 9;
/** Not defined. */
public static final int NOT_DEFINED = 101;
/**
* Converts the state to string.
*/
public static String toString(int state) {
switch (state) {
case READY_TO_CALL:
return "READY_TO_CALL";
case REGISTERING:
return "REGISTERING";
case DEREGISTERING:
return "DEREGISTERING";
case INCOMING_CALL:
return "INCOMING_CALL";
case INCOMING_CALL_ANSWERING:
return "INCOMING_CALL_ANSWERING";
case OUTGOING_CALL:
return "OUTGOING_CALL";
case OUTGOING_CALL_RING_BACK:
return "OUTGOING_CALL_RING_BACK";
case OUTGOING_CALL_CANCELING:
return "OUTGOING_CALL_CANCELING";
case IN_CALL:
return "IN_CALL";
case PINGING:
return "PINGING";
default:
return "NOT_DEFINED";
}
}
private State() {
}
}
/**
* Listener class that listens to {@link SipSession} events.
* @hide
*/
public static class Listener {
/**
* Called when an INVITE request is sent to initiate a new call.
*
* @param session the session object that carries out the transaction
*/
public void onCalling(SipSession session) {
}
/**
* Called when an INVITE request is received.
*
* @param session the session object that carries out the transaction
* @param caller the SIP profile of the caller
* @param sessionDescription the caller's session description
*/
public void onRinging(SipSession session, SipProfile caller,
String sessionDescription) {
}
/**
* Called when a RINGING response is received for the INVITE request sent
*
* @param session the session object that carries out the transaction
*/
public void onRingingBack(SipSession session) {
}
/**
* Called when the session is established.
*
* @param session the session object that is associated with the dialog
* @param sessionDescription the peer's session description
*/
public void onCallEstablished(SipSession session,
String sessionDescription) {
}
/**
* Called when the session is terminated.
*
* @param session the session object that is associated with the dialog
*/
public void onCallEnded(SipSession session) {
}
/**
* Called when the peer is busy during session initialization.
*
* @param session the session object that carries out the transaction
*/
public void onCallBusy(SipSession session) {
}
/**
* Called when an error occurs during session initialization and
* termination.
*
* @param session the session object that carries out the transaction
* @param errorCode error code defined in {@link SipErrorCode}
* @param errorMessage error message
*/
public void onError(SipSession session, int errorCode,
String errorMessage) {
}
/**
* Called when an error occurs during session modification negotiation.
*
* @param session the session object that carries out the transaction
* @param errorCode error code defined in {@link SipErrorCode}
* @param errorMessage error message
*/
public void onCallChangeFailed(SipSession session, int errorCode,
String errorMessage) {
}
/**
* Called when a registration request is sent.
*
* @param session the session object that carries out the transaction
*/
public void onRegistering(SipSession session) {
}
/**
* Called when registration is successfully done.
*
* @param session the session object that carries out the transaction
* @param duration duration in second before the registration expires
*/
public void onRegistrationDone(SipSession session, int duration) {
}
/**
* Called when the registration fails.
*
* @param session the session object that carries out the transaction
* @param errorCode error code defined in {@link SipErrorCode}
* @param errorMessage error message
*/
public void onRegistrationFailed(SipSession session, int errorCode,
String errorMessage) {
}
/**
* Called when the registration gets timed out.
*
* @param session the session object that carries out the transaction
*/
public void onRegistrationTimeout(SipSession session) {
}
}
private final ISipSession mSession;
private Listener mListener;
SipSession(ISipSession realSession) {
mSession = realSession;
if (realSession != null) {
try {
realSession.setListener(createListener());
} catch (RemoteException e) {
Log.e(TAG, "SipSession.setListener(): " + e);
}
}
}
SipSession(ISipSession realSession, Listener listener) {
this(realSession);
setListener(listener);
}
/**
* Gets the IP address of the local host on which this SIP session runs.
*
* @return the IP address of the local host
*/
public String getLocalIp() {
try {
return mSession.getLocalIp();
} catch (RemoteException e) {
Log.e(TAG, "getLocalIp(): " + e);
return "127.0.0.1";
}
}
/**
* Gets the SIP profile that this session is associated with.
*
* @return the SIP profile that this session is associated with
*/
public SipProfile getLocalProfile() {
try {
return mSession.getLocalProfile();
} catch (RemoteException e) {
Log.e(TAG, "getLocalProfile(): " + e);
return null;
}
}
/**
* Gets the SIP profile that this session is connected to. Only available
* when the session is associated with a SIP dialog.
*
* @return the SIP profile that this session is connected to
*/
public SipProfile getPeerProfile() {
try {
return mSession.getPeerProfile();
} catch (RemoteException e) {
Log.e(TAG, "getPeerProfile(): " + e);
return null;
}
}
/**
* Gets the session state. The value returned must be one of the states in
* {@link SipSessionState}.
*
* @return the session state
*/
public int getState() {
try {
return mSession.getState();
} catch (RemoteException e) {
Log.e(TAG, "getState(): " + e);
return State.NOT_DEFINED;
}
}
/**
* Checks if the session is in a call.
*
* @return true if the session is in a call
*/
public boolean isInCall() {
try {
return mSession.isInCall();
} catch (RemoteException e) {
Log.e(TAG, "isInCall(): " + e);
return false;
}
}
/**
* Gets the call ID of the session.
*
* @return the call ID
*/
public String getCallId() {
try {
return mSession.getCallId();
} catch (RemoteException e) {
Log.e(TAG, "getCallId(): " + e);
return null;
}
}
/**
* Sets the listener to listen to the session events. A {@code SipSession}
* can only hold one listener at a time. Subsequent calls to this method
* override the previous listener.
*
* @param listener to listen to the session events of this object
*/
public void setListener(Listener listener) {
mListener = listener;
}
/**
* Performs registration to the server specified by the associated local
* profile. The session listener is called back upon success or failure of
* registration. The method is only valid to call when the session state is
* in {@link SipSessionState#READY_TO_CALL}.
*
* @param duration duration in second before the registration expires
* @see Listener
*/
public void register(int duration) {
try {
mSession.register(duration);
} catch (RemoteException e) {
Log.e(TAG, "register(): " + e);
}
}
/**
* Performs unregistration to the server specified by the associated local
* profile. Unregistration is technically the same as registration with zero
* expiration duration. The session listener is called back upon success or
* failure of unregistration. The method is only valid to call when the
* session state is in {@link SipSessionState#READY_TO_CALL}.
*
* @see Listener
*/
public void unregister() {
try {
mSession.unregister();
} catch (RemoteException e) {
Log.e(TAG, "unregister(): " + e);
}
}
/**
* Initiates a call to the specified profile. The session listener is called
* back upon defined session events. The method is only valid to call when
* the session state is in {@link SipSessionState#READY_TO_CALL}.
*
* @param callee the SIP profile to make the call to
* @param sessionDescription the session description of this call
* @param timeout the session will be timed out if the call is not
* established within {@code timeout} seconds. Default value (defined
* by SIP protocol) is used if {@code timeout} is zero or negative.
* @see Listener
*/
public void makeCall(SipProfile callee, String sessionDescription,
int timeout) {
try {
mSession.makeCall(callee, sessionDescription, timeout);
} catch (RemoteException e) {
Log.e(TAG, "makeCall(): " + e);
}
}
/**
* Answers an incoming call with the specified session description. The
* method is only valid to call when the session state is in
* {@link SipSessionState#INCOMING_CALL}.
*
* @param sessionDescription the session description to answer this call
* @param timeout the session will be timed out if the call is not
* established within {@code timeout} seconds. Default value (defined
* by SIP protocol) is used if {@code timeout} is zero or negative.
*/
public void answerCall(String sessionDescription, int timeout) {
try {
mSession.answerCall(sessionDescription, timeout);
} catch (RemoteException e) {
Log.e(TAG, "answerCall(): " + e);
}
}
/**
* Ends an established call, terminates an outgoing call or rejects an
* incoming call. The method is only valid to call when the session state is
* in {@link SipSessionState#IN_CALL},
* {@link SipSessionState#INCOMING_CALL},
* {@link SipSessionState#OUTGOING_CALL} or
* {@link SipSessionState#OUTGOING_CALL_RING_BACK}.
*/
public void endCall() {
try {
mSession.endCall();
} catch (RemoteException e) {
Log.e(TAG, "endCall(): " + e);
}
}
/**
* Changes the session description during a call. The method is only valid
* to call when the session state is in {@link SipSessionState#IN_CALL}.
*
* @param sessionDescription the new session description
* @param timeout the session will be timed out if the call is not
* established within {@code timeout} seconds. Default value (defined
* by SIP protocol) is used if {@code timeout} is zero or negative.
*/
public void changeCall(String sessionDescription, int timeout) {
try {
mSession.changeCall(sessionDescription, timeout);
} catch (RemoteException e) {
Log.e(TAG, "changeCall(): " + e);
}
}
ISipSession getRealSession() {
return mSession;
}
private ISipSessionListener createListener() {
return new ISipSessionListener.Stub() {
public void onCalling(ISipSession session) {
if (mListener != null) {
mListener.onCalling(SipSession.this);
}
}
public void onRinging(ISipSession session, SipProfile caller,
String sessionDescription) {
if (mListener != null) {
mListener.onRinging(SipSession.this, caller,
sessionDescription);
}
}
public void onRingingBack(ISipSession session) {
if (mListener != null) {
mListener.onRingingBack(SipSession.this);
}
}
public void onCallEstablished(ISipSession session,
String sessionDescription) {
if (mListener != null) {
mListener.onCallEstablished(SipSession.this,
sessionDescription);
}
}
public void onCallEnded(ISipSession session) {
if (mListener != null) {
mListener.onCallEnded(SipSession.this);
}
}
public void onCallBusy(ISipSession session) {
if (mListener != null) {
mListener.onCallBusy(SipSession.this);
}
}
public void onCallChangeFailed(ISipSession session, int errorCode,
String message) {
if (mListener != null) {
mListener.onCallChangeFailed(SipSession.this, errorCode,
message);
}
}
public void onError(ISipSession session, int errorCode, String message) {
if (mListener != null) {
mListener.onError(SipSession.this, errorCode, message);
}
}
public void onRegistering(ISipSession session) {
if (mListener != null) {
mListener.onRegistering(SipSession.this);
}
}
public void onRegistrationDone(ISipSession session, int duration) {
if (mListener != null) {
mListener.onRegistrationDone(SipSession.this, duration);
}
}
public void onRegistrationFailed(ISipSession session, int errorCode,
String message) {
if (mListener != null) {
mListener.onRegistrationFailed(SipSession.this, errorCode,
message);
}
}
public void onRegistrationTimeout(ISipSession session) {
if (mListener != null) {
mListener.onRegistrationTimeout(SipSession.this);
}
}
};
}
}

View File

@ -1,94 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net.sip;
/**
* Defines {@link ISipSession} states.
* @hide
*/
public class SipSessionState {
/** When session is ready to initiate a call or transaction. */
public static final int READY_TO_CALL = 0;
/** When the registration request is sent out. */
public static final int REGISTERING = 1;
/** When the unregistration request is sent out. */
public static final int DEREGISTERING = 2;
/** When an INVITE request is received. */
public static final int INCOMING_CALL = 3;
/** When an OK response is sent for the INVITE request received. */
public static final int INCOMING_CALL_ANSWERING = 4;
/** When an INVITE request is sent. */
public static final int OUTGOING_CALL = 5;
/** When a RINGING response is received for the INVITE request sent. */
public static final int OUTGOING_CALL_RING_BACK = 6;
/** When a CANCEL request is sent for the INVITE request sent. */
public static final int OUTGOING_CALL_CANCELING = 7;
/** When a call is established. */
public static final int IN_CALL = 8;
/** Some error occurs when making a remote call to {@link ISipSession}. */
public static final int REMOTE_ERROR = 9;
/** When an OPTIONS request is sent. */
public static final int PINGING = 10;
/** Not defined. */
public static final int NOT_DEFINED = 101;
/**
* Converts the state to string.
*/
public static String toString(int state) {
switch (state) {
case READY_TO_CALL:
return "READY_TO_CALL";
case REGISTERING:
return "REGISTERING";
case DEREGISTERING:
return "DEREGISTERING";
case INCOMING_CALL:
return "INCOMING_CALL";
case INCOMING_CALL_ANSWERING:
return "INCOMING_CALL_ANSWERING";
case OUTGOING_CALL:
return "OUTGOING_CALL";
case OUTGOING_CALL_RING_BACK:
return "OUTGOING_CALL_RING_BACK";
case OUTGOING_CALL_CANCELING:
return "OUTGOING_CALL_CANCELING";
case IN_CALL:
return "IN_CALL";
case REMOTE_ERROR:
return "REMOTE_ERROR";
case PINGING:
return "PINGING";
default:
return "NOT_DEFINED";
}
}
private SipSessionState() {
}
}