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.SipErrorCode;
import android.net.sip.SipManager; import android.net.sip.SipManager;
import android.net.sip.SipProfile; import android.net.sip.SipProfile;
import android.net.sip.SipSession;
import android.net.sip.SipSessionAdapter; import android.net.sip.SipSessionAdapter;
import android.net.sip.SipSessionState;
import android.net.wifi.WifiManager; import android.net.wifi.WifiManager;
import android.os.Binder; import android.os.Binder;
import android.os.Bundle; import android.os.Bundle;
@ -143,7 +143,7 @@ public final class SipService extends ISipService.Stub {
} }
private void openToReceiveCalls(SipProfile localProfile) { 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, public synchronized void open3(SipProfile localProfile,
@ -255,15 +255,15 @@ public final class SipService extends ISipService.Stub {
private void notifyProfileAdded(SipProfile localProfile) { private void notifyProfileAdded(SipProfile localProfile) {
if (DEBUG) Log.d(TAG, "notify: profile added: " + localProfile); if (DEBUG) Log.d(TAG, "notify: profile added: " + localProfile);
Intent intent = new Intent(SipManager.SIP_ADD_PHONE_ACTION); Intent intent = new Intent(SipManager.ACTION_SIP_ADD_PHONE);
intent.putExtra(SipManager.LOCAL_URI_KEY, localProfile.getUriString()); intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
mContext.sendBroadcast(intent); mContext.sendBroadcast(intent);
} }
private void notifyProfileRemoved(SipProfile localProfile) { private void notifyProfileRemoved(SipProfile localProfile) {
if (DEBUG) Log.d(TAG, "notify: profile removed: " + localProfile); if (DEBUG) Log.d(TAG, "notify: profile removed: " + localProfile);
Intent intent = new Intent(SipManager.SIP_REMOVE_PHONE_ACTION); Intent intent = new Intent(SipManager.ACTION_SIP_REMOVE_PHONE);
intent.putExtra(SipManager.LOCAL_URI_KEY, localProfile.getUriString()); intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
mContext.sendBroadcast(intent); mContext.sendBroadcast(intent);
} }
@ -474,8 +474,8 @@ public final class SipService extends ISipService.Stub {
// send out incoming call broadcast // send out incoming call broadcast
addPendingSession(session); addPendingSession(session);
Intent intent = SipManager.createIncomingCallBroadcast( Intent intent = SipManager.createIncomingCallBroadcast(
mIncomingCallBroadcastAction, session.getCallId(), session.getCallId(), sessionDescription)
sessionDescription); .setAction(mIncomingCallBroadcastAction);
if (DEBUG) Log.d(TAG, " ringing~~ " + getUri() + ": " if (DEBUG) Log.d(TAG, " ringing~~ " + getUri() + ": "
+ caller.getUri() + ": " + session.getCallId() + caller.getUri() + ": " + session.getCallId()
+ " " + mIncomingCallBroadcastAction); + " " + mIncomingCallBroadcastAction);
@ -613,10 +613,10 @@ public final class SipService extends ISipService.Stub {
try { try {
int state = (mSession == null) int state = (mSession == null)
? SipSessionState.READY_TO_CALL ? SipSession.State.READY_TO_CALL
: mSession.getState(); : mSession.getState();
if ((state == SipSessionState.REGISTERING) if ((state == SipSession.State.REGISTERING)
|| (state == SipSessionState.DEREGISTERING)) { || (state == SipSession.State.DEREGISTERING)) {
mProxy.onRegistering(mSession); mProxy.onRegistering(mSession);
} else if (mRegistered) { } else if (mRegistered) {
int duration = (int) int duration = (int)
@ -1138,7 +1138,8 @@ public final class SipService extends ISipService.Stub {
event.mTriggerTime += event.mPeriod; event.mTriggerTime += event.mPeriod;
// run the callback in a new thread to prevent deadlock // run the callback in a new thread to prevent deadlock
new Thread(event.mCallback).start(); new Thread(event.mCallback, "SipServiceTimerCallbackThread")
.start();
} }
if (DEBUG_TIMER) { if (DEBUG_TIMER) {
Log.d(TAG, "after timeout execution"); 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.SessionDescription;
import android.net.sip.SipErrorCode; import android.net.sip.SipErrorCode;
import android.net.sip.SipProfile; import android.net.sip.SipProfile;
import android.net.sip.SipSession;
import android.net.sip.SipSessionAdapter; import android.net.sip.SipSessionAdapter;
import android.net.sip.SipSessionState;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
@ -121,7 +121,7 @@ class SipSessionGroup implements SipListener {
reset(localIp); reset(localIp);
} }
void reset(String localIp) throws SipException, IOException { synchronized void reset(String localIp) throws SipException, IOException {
mLocalIp = localIp; mLocalIp = localIp;
if (localIp == null) return; if (localIp == null) return;
@ -301,7 +301,7 @@ class SipSessionGroup implements SipListener {
boolean processed = (session != null) && session.process(event); boolean processed = (session != null) && session.process(event);
if (isLoggable && processed) { if (isLoggable && processed) {
Log.d(TAG, "new state after: " Log.d(TAG, "new state after: "
+ SipSessionState.toString(session.mState)); + SipSession.State.toString(session.mState));
} }
} catch (Throwable e) { } catch (Throwable e) {
Log.w(TAG, "event process error: " + event, e); Log.w(TAG, "event process error: " + event, e);
@ -332,7 +332,7 @@ class SipSessionGroup implements SipListener {
public boolean process(EventObject evt) throws SipException { public boolean process(EventObject evt) throws SipException {
if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~ " + this + ": " if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~ " + this + ": "
+ SipSessionState.toString(mState) + ": processing " + SipSession.State.toString(mState) + ": processing "
+ log(evt)); + log(evt));
if (isRequestEvent(Request.INVITE, evt)) { if (isRequestEvent(Request.INVITE, evt)) {
RequestEvent event = (RequestEvent) evt; RequestEvent event = (RequestEvent) evt;
@ -342,7 +342,7 @@ class SipSessionGroup implements SipListener {
newSession.mDialog = newSession.mServerTransaction.getDialog(); newSession.mDialog = newSession.mServerTransaction.getDialog();
newSession.mInviteReceived = event; newSession.mInviteReceived = event;
newSession.mPeerProfile = createPeerProfile(event.getRequest()); newSession.mPeerProfile = createPeerProfile(event.getRequest());
newSession.mState = SipSessionState.INCOMING_CALL; newSession.mState = SipSession.State.INCOMING_CALL;
newSession.mPeerSessionDescription = newSession.mPeerSessionDescription =
extractContent(event.getRequest()); extractContent(event.getRequest());
addSipSession(newSession); addSipSession(newSession);
@ -361,7 +361,7 @@ class SipSessionGroup implements SipListener {
class SipSessionImpl extends ISipSession.Stub { class SipSessionImpl extends ISipSession.Stub {
SipProfile mPeerProfile; SipProfile mPeerProfile;
SipSessionListenerProxy mProxy = new SipSessionListenerProxy(); SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
int mState = SipSessionState.READY_TO_CALL; int mState = SipSession.State.READY_TO_CALL;
RequestEvent mInviteReceived; RequestEvent mInviteReceived;
Dialog mDialog; Dialog mDialog;
ServerTransaction mServerTransaction; ServerTransaction mServerTransaction;
@ -381,7 +381,7 @@ class SipSessionGroup implements SipListener {
sleep(timeout); sleep(timeout);
if (mRunning) timeout(); if (mRunning) timeout();
} }
}).start(); }, "SipSessionTimerThread").start();
} }
synchronized void cancel() { synchronized void cancel() {
@ -416,7 +416,7 @@ class SipSessionGroup implements SipListener {
mInCall = false; mInCall = false;
removeSipSession(this); removeSipSession(this);
mPeerProfile = null; mPeerProfile = null;
mState = SipSessionState.READY_TO_CALL; mState = SipSession.State.READY_TO_CALL;
mInviteReceived = null; mInviteReceived = null;
mDialog = null; mDialog = null;
mServerTransaction = null; mServerTransaction = null;
@ -473,7 +473,7 @@ class SipSessionGroup implements SipListener {
onError(e); onError(e);
} }
} }
}).start(); }, "SipSessionAsyncCmdThread").start();
} }
public void makeCall(SipProfile peerProfile, String sessionDescription, public void makeCall(SipProfile peerProfile, String sessionDescription,
@ -523,10 +523,10 @@ class SipSessionGroup implements SipListener {
} }
public void sendKeepAlive() { public void sendKeepAlive() {
mState = SipSessionState.PINGING; mState = SipSession.State.PINGING;
try { try {
processCommand(new OptionsCommand()); processCommand(new OptionsCommand());
while (SipSessionState.PINGING == mState) { while (SipSession.State.PINGING == mState) {
Thread.sleep(1000); Thread.sleep(1000);
} }
} catch (SipException e) { } catch (SipException e) {
@ -553,7 +553,7 @@ class SipSessionGroup implements SipListener {
try { try {
String s = super.toString(); String s = super.toString();
return s.substring(s.indexOf("@")) + ":" return s.substring(s.indexOf("@")) + ":"
+ SipSessionState.toString(mState); + SipSession.State.toString(mState);
} catch (Throwable e) { } catch (Throwable e) {
return super.toString(); return super.toString();
} }
@ -561,7 +561,7 @@ class SipSessionGroup implements SipListener {
public boolean process(EventObject evt) throws SipException { public boolean process(EventObject evt) throws SipException {
if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~ " + this + ": " if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~ " + this + ": "
+ SipSessionState.toString(mState) + ": processing " + SipSession.State.toString(mState) + ": processing "
+ log(evt)); + log(evt));
synchronized (SipSessionGroup.this) { synchronized (SipSessionGroup.this) {
if (isClosed()) return false; if (isClosed()) return false;
@ -577,30 +577,30 @@ class SipSessionGroup implements SipListener {
boolean processed; boolean processed;
switch (mState) { switch (mState) {
case SipSessionState.REGISTERING: case SipSession.State.REGISTERING:
case SipSessionState.DEREGISTERING: case SipSession.State.DEREGISTERING:
processed = registeringToReady(evt); processed = registeringToReady(evt);
break; break;
case SipSessionState.PINGING: case SipSession.State.PINGING:
processed = keepAliveProcess(evt); processed = keepAliveProcess(evt);
break; break;
case SipSessionState.READY_TO_CALL: case SipSession.State.READY_TO_CALL:
processed = readyForCall(evt); processed = readyForCall(evt);
break; break;
case SipSessionState.INCOMING_CALL: case SipSession.State.INCOMING_CALL:
processed = incomingCall(evt); processed = incomingCall(evt);
break; break;
case SipSessionState.INCOMING_CALL_ANSWERING: case SipSession.State.INCOMING_CALL_ANSWERING:
processed = incomingCallToInCall(evt); processed = incomingCallToInCall(evt);
break; break;
case SipSessionState.OUTGOING_CALL: case SipSession.State.OUTGOING_CALL:
case SipSessionState.OUTGOING_CALL_RING_BACK: case SipSession.State.OUTGOING_CALL_RING_BACK:
processed = outgoingCall(evt); processed = outgoingCall(evt);
break; break;
case SipSessionState.OUTGOING_CALL_CANCELING: case SipSession.State.OUTGOING_CALL_CANCELING:
processed = outgoingCallToReady(evt); processed = outgoingCallToReady(evt);
break; break;
case SipSessionState.IN_CALL: case SipSession.State.IN_CALL:
processed = inCall(evt); processed = inCall(evt);
break; break;
default: default:
@ -650,8 +650,8 @@ class SipSessionGroup implements SipListener {
private void processTransactionTerminated( private void processTransactionTerminated(
TransactionTerminatedEvent event) { TransactionTerminatedEvent event) {
switch (mState) { switch (mState) {
case SipSessionState.IN_CALL: case SipSession.State.IN_CALL:
case SipSessionState.READY_TO_CALL: case SipSession.State.READY_TO_CALL:
Log.d(TAG, "Transaction terminated; do nothing"); Log.d(TAG, "Transaction terminated; do nothing");
break; break;
default: default:
@ -670,27 +670,27 @@ class SipSessionGroup implements SipListener {
? event.getServerTransaction() ? event.getServerTransaction()
: event.getClientTransaction(); : event.getClientTransaction();
if ((current != target) && (mState != SipSessionState.PINGING)) { if ((current != target) && (mState != SipSession.State.PINGING)) {
Log.d(TAG, "not the current transaction; current=" + current Log.d(TAG, "not the current transaction; current=" + current
+ ", timed out=" + target); + ", timed out=" + target);
return; return;
} }
switch (mState) { switch (mState) {
case SipSessionState.REGISTERING: case SipSession.State.REGISTERING:
case SipSessionState.DEREGISTERING: case SipSession.State.DEREGISTERING:
reset(); reset();
mProxy.onRegistrationTimeout(this); mProxy.onRegistrationTimeout(this);
break; break;
case SipSessionState.INCOMING_CALL: case SipSession.State.INCOMING_CALL:
case SipSessionState.INCOMING_CALL_ANSWERING: case SipSession.State.INCOMING_CALL_ANSWERING:
case SipSessionState.OUTGOING_CALL: case SipSession.State.OUTGOING_CALL:
case SipSessionState.OUTGOING_CALL_CANCELING: case SipSession.State.OUTGOING_CALL_CANCELING:
onError(SipErrorCode.TIME_OUT, event.toString()); onError(SipErrorCode.TIME_OUT, event.toString());
break; break;
case SipSessionState.PINGING: case SipSession.State.PINGING:
reset(); reset();
mReRegisterFlag = true; mReRegisterFlag = true;
mState = SipSessionState.READY_TO_CALL; mState = SipSession.State.READY_TO_CALL;
break; break;
default: default:
@ -764,7 +764,7 @@ class SipSessionGroup implements SipListener {
switch (statusCode) { switch (statusCode) {
case Response.OK: case Response.OK:
int state = mState; int state = mState;
onRegistrationDone((state == SipSessionState.REGISTERING) onRegistrationDone((state == SipSession.State.REGISTERING)
? getExpiryTime(((ResponseEvent) evt).getResponse()) ? getExpiryTime(((ResponseEvent) evt).getResponse())
: -1); : -1);
mLastNonce = null; mLastNonce = null;
@ -851,7 +851,7 @@ class SipSessionGroup implements SipListener {
generateTag()); generateTag());
mDialog = mClientTransaction.getDialog(); mDialog = mClientTransaction.getDialog();
addSipSession(this); addSipSession(this);
mState = SipSessionState.OUTGOING_CALL; mState = SipSession.State.OUTGOING_CALL;
mProxy.onCalling(this); mProxy.onCalling(this);
startSessionTimer(cmd.getTimeout()); startSessionTimer(cmd.getTimeout());
return true; return true;
@ -861,7 +861,7 @@ class SipSessionGroup implements SipListener {
generateTag(), duration); generateTag(), duration);
mDialog = mClientTransaction.getDialog(); mDialog = mClientTransaction.getDialog();
addSipSession(this); addSipSession(this);
mState = SipSessionState.REGISTERING; mState = SipSession.State.REGISTERING;
mProxy.onRegistering(this); mProxy.onRegistering(this);
return true; return true;
} else if (DEREGISTER == evt) { } else if (DEREGISTER == evt) {
@ -869,7 +869,7 @@ class SipSessionGroup implements SipListener {
generateTag(), 0); generateTag(), 0);
mDialog = mClientTransaction.getDialog(); mDialog = mClientTransaction.getDialog();
addSipSession(this); addSipSession(this);
mState = SipSessionState.DEREGISTERING; mState = SipSession.State.DEREGISTERING;
mProxy.onRegistering(this); mProxy.onRegistering(this);
return true; return true;
} }
@ -884,7 +884,7 @@ class SipSessionGroup implements SipListener {
mLocalProfile, mLocalProfile,
((MakeCallCommand) evt).getSessionDescription(), ((MakeCallCommand) evt).getSessionDescription(),
mServerTransaction); mServerTransaction);
mState = SipSessionState.INCOMING_CALL_ANSWERING; mState = SipSession.State.INCOMING_CALL_ANSWERING;
startSessionTimer(((MakeCallCommand) evt).getTimeout()); startSessionTimer(((MakeCallCommand) evt).getTimeout());
return true; return true;
} else if (END_CALL == evt) { } else if (END_CALL == evt) {
@ -925,8 +925,8 @@ class SipSessionGroup implements SipListener {
int statusCode = response.getStatusCode(); int statusCode = response.getStatusCode();
switch (statusCode) { switch (statusCode) {
case Response.RINGING: case Response.RINGING:
if (mState == SipSessionState.OUTGOING_CALL) { if (mState == SipSession.State.OUTGOING_CALL) {
mState = SipSessionState.OUTGOING_CALL_RING_BACK; mState = SipSession.State.OUTGOING_CALL_RING_BACK;
mProxy.onRingingBack(this); mProxy.onRingingBack(this);
cancelSessionTimer(); cancelSessionTimer();
} }
@ -969,7 +969,7 @@ class SipSessionGroup implements SipListener {
// response comes back yet. We are cheating for not checking // response comes back yet. We are cheating for not checking
// response. // response.
mSipHelper.sendCancel(mClientTransaction); mSipHelper.sendCancel(mClientTransaction);
mState = SipSessionState.OUTGOING_CALL_CANCELING; mState = SipSession.State.OUTGOING_CALL_CANCELING;
startSessionTimer(CANCEL_CALL_TIMER); startSessionTimer(CANCEL_CALL_TIMER);
return true; return true;
} }
@ -1025,7 +1025,7 @@ class SipSessionGroup implements SipListener {
} else if (isRequestEvent(Request.INVITE, evt)) { } else if (isRequestEvent(Request.INVITE, evt)) {
// got Re-INVITE // got Re-INVITE
RequestEvent event = mInviteReceived = (RequestEvent) evt; RequestEvent event = mInviteReceived = (RequestEvent) evt;
mState = SipSessionState.INCOMING_CALL; mState = SipSession.State.INCOMING_CALL;
mPeerSessionDescription = extractContent(event.getRequest()); mPeerSessionDescription = extractContent(event.getRequest());
mServerTransaction = null; mServerTransaction = null;
mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription); mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription);
@ -1038,7 +1038,7 @@ class SipSessionGroup implements SipListener {
// to change call // to change call
mClientTransaction = mSipHelper.sendReinvite(mDialog, mClientTransaction = mSipHelper.sendReinvite(mDialog,
((MakeCallCommand) evt).getSessionDescription()); ((MakeCallCommand) evt).getSessionDescription());
mState = SipSessionState.OUTGOING_CALL; mState = SipSession.State.OUTGOING_CALL;
startSessionTimer(((MakeCallCommand) evt).getTimeout()); startSessionTimer(((MakeCallCommand) evt).getTimeout());
return true; return true;
} }
@ -1066,14 +1066,14 @@ class SipSessionGroup implements SipListener {
} }
private void establishCall() { private void establishCall() {
mState = SipSessionState.IN_CALL; mState = SipSession.State.IN_CALL;
mInCall = true; mInCall = true;
cancelSessionTimer(); cancelSessionTimer();
mProxy.onCallEstablished(this, mPeerSessionDescription); mProxy.onCallEstablished(this, mPeerSessionDescription);
} }
private void fallbackToPreviousInCall(int errorCode, String message) { private void fallbackToPreviousInCall(int errorCode, String message) {
mState = SipSessionState.IN_CALL; mState = SipSession.State.IN_CALL;
mProxy.onCallChangeFailed(this, errorCode, message); mProxy.onCallChangeFailed(this, errorCode, message);
} }
@ -1095,8 +1095,8 @@ class SipSessionGroup implements SipListener {
private void onError(int errorCode, String message) { private void onError(int errorCode, String message) {
cancelSessionTimer(); cancelSessionTimer();
switch (mState) { switch (mState) {
case SipSessionState.REGISTERING: case SipSession.State.REGISTERING:
case SipSessionState.DEREGISTERING: case SipSession.State.DEREGISTERING:
onRegistrationFailed(errorCode, message); onRegistrationFailed(errorCode, message);
break; break;
default: default:
@ -1270,7 +1270,7 @@ class SipSessionGroup implements SipListener {
private static boolean isLoggable(SipSessionImpl s) { private static boolean isLoggable(SipSessionImpl s) {
if (s != null) { if (s != null) {
switch (s.mState) { switch (s.mState) {
case SipSessionState.PINGING: case SipSession.State.PINGING:
return DEBUG_PING; return DEBUG_PING;
} }
} }

View File

@ -40,7 +40,7 @@ class SipSessionListenerProxy extends ISipSessionListener.Stub {
// One thread for each calling back. // One thread for each calling back.
// Note: Guarantee ordering if the issue becomes important. Currently, // Note: Guarantee ordering if the issue becomes important. Currently,
// the chance of handling two callback events at a time is none. // 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) { 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.SipException;
import android.net.sip.SipManager; import android.net.sip.SipManager;
import android.net.sip.SipProfile; import android.net.sip.SipProfile;
import android.net.sip.SipSessionState; import android.net.sip.SipSession;
import android.os.AsyncResult; import android.os.AsyncResult;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
@ -92,7 +92,7 @@ public class SipPhone extends SipPhoneBase {
foregroundCall = new SipCall(); foregroundCall = new SipCall();
backgroundCall = new SipCall(); backgroundCall = new SipCall();
mProfile = profile; mProfile = profile;
mSipManager = SipManager.getInstance(context); mSipManager = SipManager.newInstance(context);
// FIXME: what's this for SIP? // FIXME: what's this for SIP?
//Change the system property //Change the system property
@ -707,8 +707,8 @@ public class SipPhone extends SipPhoneBase {
void dial() throws SipException { void dial() throws SipException {
setState(Call.State.DIALING); setState(Call.State.DIALING);
mSipAudioCall = mSipManager.makeAudioCall(mContext, mProfile, mSipAudioCall = mSipManager.makeAudioCall(mProfile, mPeer, null,
mPeer, null, SESSION_TIMEOUT); SESSION_TIMEOUT);
mSipAudioCall.setRingbackToneEnabled(false); mSipAudioCall.setRingbackToneEnabled(false);
mSipAudioCall.setListener(mAdapter); mSipAudioCall.setListener(mAdapter);
} }
@ -808,20 +808,20 @@ public class SipPhone extends SipPhoneBase {
if (sipAudioCall.isOnHold()) return Call.State.HOLDING; if (sipAudioCall.isOnHold()) return Call.State.HOLDING;
int sessionState = sipAudioCall.getState(); int sessionState = sipAudioCall.getState();
switch (sessionState) { switch (sessionState) {
case SipSessionState.READY_TO_CALL: return Call.State.IDLE; case SipSession.State.READY_TO_CALL: return Call.State.IDLE;
case SipSessionState.INCOMING_CALL: case SipSession.State.INCOMING_CALL:
case SipSessionState.INCOMING_CALL_ANSWERING: return Call.State.INCOMING; case SipSession.State.INCOMING_CALL_ANSWERING: return Call.State.INCOMING;
case SipSessionState.OUTGOING_CALL: return Call.State.DIALING; case SipSession.State.OUTGOING_CALL: return Call.State.DIALING;
case SipSessionState.OUTGOING_CALL_RING_BACK: return Call.State.ALERTING; case SipSession.State.OUTGOING_CALL_RING_BACK: return Call.State.ALERTING;
case SipSessionState.OUTGOING_CALL_CANCELING: return Call.State.DISCONNECTING; case SipSession.State.OUTGOING_CALL_CANCELING: return Call.State.DISCONNECTING;
case SipSessionState.IN_CALL: return Call.State.ACTIVE; case SipSession.State.IN_CALL: return Call.State.ACTIVE;
default: default:
Log.w(LOG_TAG, "illegal connection state: " + sessionState); Log.w(LOG_TAG, "illegal connection state: " + sessionState);
return Call.State.DISCONNECTED; 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 onCallEnded(Connection.DisconnectCause cause);
protected abstract void onError(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 * The class provides API for various SIP related tasks. Specifically, the API
* allows an application to: * allows an application to:
* <ul> * <ul>
* <li>register a {@link SipProfile} to have the background SIP service listen * <li>open a {@link SipProfile} to get ready for making outbound calls or have
* to incoming calls and broadcast them with registered command string. See * the background SIP service listen to incoming calls and broadcast them
* with registered command string. See
* {@link #open(SipProfile, String, SipRegistrationListener)}, * {@link #open(SipProfile, String, SipRegistrationListener)},
* {@link #open(SipProfile)}, {@link #close}, {@link #isOpened} and * {@link #open(SipProfile)}, {@link #close}, {@link #isOpened} and
* {@link #isRegistered}. It also facilitates handling of the incoming call * {@link #isRegistered}. It also facilitates handling of the incoming call
@ -40,39 +41,59 @@ import java.text.ParseException;
* {@link #getOfferSessionDescription} and {@link #takeAudioCall}.</li> * {@link #getOfferSessionDescription} and {@link #takeAudioCall}.</li>
* <li>make/take SIP-based audio calls. See * <li>make/take SIP-based audio calls. See
* {@link #makeAudioCall} and {@link #takeAudioCall}.</li> * {@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> * {@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> * {@link #createSipSession}.</li>
* </ul> * </ul>
* @hide * @hide
*/ */
public class SipManager { 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"; "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"; "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"; "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"; /** Part of the incoming call intent. */
private static final String OFFER_SD_KEY = "OfferSD"; 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 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 * @return the manager instance or null if SIP API is not supported
*/ */
public static SipManager getInstance(Context context) { public static SipManager newInstance(Context context) {
return (isApiSupported(context) ? new SipManager() : null); return (isApiSupported(context) ? new SipManager(context) : null);
} }
/** /**
@ -80,7 +101,7 @@ public class SipManager {
*/ */
public static boolean isApiSupported(Context context) { public static boolean isApiSupported(Context context) {
return true; return true;
/* /* TODO: uncomment this before ship
return context.getPackageManager().hasSystemFeature( return context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SIP); PackageManager.FEATURE_SIP);
*/ */
@ -91,7 +112,7 @@ public class SipManager {
*/ */
public static boolean isVoipSupported(Context context) { public static boolean isVoipSupported(Context context) {
return true; return true;
/* /* TODO: uncomment this before ship
return context.getPackageManager().hasSystemFeature( return context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SIP_VOIP) && isApiSupported(context); PackageManager.FEATURE_SIP_VOIP) && isApiSupported(context);
*/ */
@ -105,23 +126,21 @@ public class SipManager {
com.android.internal.R.bool.config_sip_wifi_only); com.android.internal.R.bool.config_sip_wifi_only);
} }
private SipManager() { private SipManager(Context context) {
mContext = context;
createSipService(); createSipService();
} }
private void createSipService() { private void createSipService() {
if (mSipService != null) return;
IBinder b = ServiceManager.getService(Context.SIP_SERVICE); IBinder b = ServiceManager.getService(Context.SIP_SERVICE);
mSipService = ISipService.Stub.asInterface(b); mSipService = ISipService.Stub.asInterface(b);
} }
/** /**
* Opens the profile for making calls and/or receiving calls. Subsequent * Opens the profile for making calls. The caller may make subsequent calls
* SIP calls can be made through the default phone UI. The caller may also * through {@link #makeAudioCall}. If one also wants to receive calls on the
* make subsequent calls through {@link #makeAudioCall}. * profile, use {@link #open(SipProfile, String, SipRegistrationListener)}
* If the receiving-call option is enabled in the profile, the SIP service * instead.
* will register the profile to the corresponding server periodically in
* order to receive calls from the server.
* *
* @param localProfile the SIP profile to make calls from * @param localProfile the SIP profile to make calls from
* @throws SipException if the profile contains incorrect settings or * @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 * Opens the profile for making calls and/or receiving calls. The caller may
* SIP calls can be made through the default phone UI. The caller may also * make subsequent calls through {@link #makeAudioCall}. If the
* make subsequent calls through {@link #makeAudioCall}. * auto-registration option is enabled in the profile, the SIP service
* If the receiving-call option is enabled in the profile, the SIP service * will register the profile to the corresponding SIP provider periodically
* will register the profile to the corresponding server periodically in * in order to receive calls from the provider.
* order to receive calls from the server.
* *
* @param localProfile the SIP profile to receive incoming calls for * @param localProfile the SIP profile to receive incoming calls for
* @param incomingCallBroadcastAction the action to be broadcast when an * @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 * @param localProfileUri the URI of the profile in question
* @return true if the profile is enabled to receive calls * @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 * Checks if the SIP service has successfully registered the profile to the
* receiving calls. * 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 * @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 * @throws SipException if calling the SIP service results in an error
*/ */
public boolean isRegistered(String localProfileUri) throws SipException { public boolean isRegistered(String localProfileUri) throws SipException {
@ -231,7 +255,6 @@ public class SipManager {
* {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)} * {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
* will be called. * will be called.
* *
* @param context context to create a {@link SipAudioCall} object
* @param localProfile the SIP profile to make the call from * @param localProfile the SIP profile to make the call from
* @param peerProfile the SIP profile to make the call to * @param peerProfile the SIP profile to make the call to
* @param listener to listen to the call events from {@link SipAudioCall}; * @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 * @throws SipException if calling the SIP service results in an error
* @see SipAudioCall.Listener.onError * @see SipAudioCall.Listener.onError
*/ */
public SipAudioCall makeAudioCall(Context context, SipProfile localProfile, public SipAudioCall makeAudioCall(SipProfile localProfile,
SipProfile peerProfile, SipAudioCall.Listener listener, int timeout) SipProfile peerProfile, SipAudioCall.Listener listener, int timeout)
throws SipException { throws SipException {
SipAudioCall call = new SipAudioCallImpl(context, localProfile); SipAudioCall call = new SipAudioCall(mContext, localProfile);
call.setListener(listener); call.setListener(listener);
call.makeCall(peerProfile, this, timeout); call.makeCall(peerProfile, this, timeout);
return call; return call;
@ -257,7 +280,6 @@ public class SipManager {
* {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)} * {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
* will be called. * 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 localProfileUri URI of the SIP profile to make the call from
* @param peerProfileUri URI of the SIP profile to make the call to * @param peerProfileUri URI of the SIP profile to make the call to
* @param listener to listen to the call events from {@link SipAudioCall}; * @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 * @throws SipException if calling the SIP service results in an error
* @see SipAudioCall.Listener.onError * @see SipAudioCall.Listener.onError
*/ */
public SipAudioCall makeAudioCall(Context context, String localProfileUri, public SipAudioCall makeAudioCall(String localProfileUri,
String peerProfileUri, SipAudioCall.Listener listener, int timeout) String peerProfileUri, SipAudioCall.Listener listener, int timeout)
throws SipException { throws SipException {
try { try {
return makeAudioCall(context, return makeAudioCall(
new SipProfile.Builder(localProfileUri).build(), new SipProfile.Builder(localProfileUri).build(),
new SipProfile.Builder(peerProfileUri).build(), listener, new SipProfile.Builder(peerProfileUri).build(), listener,
timeout); timeout);
@ -281,15 +303,14 @@ public class SipManager {
} }
/** /**
* The method calls {@code takeAudioCall(context, incomingCallIntent, * The method calls {@code takeAudioCall(incomingCallIntent,
* listener, true}. * listener, true}.
* *
* @see #takeAudioCall(Context, Intent, SipAudioCall.Listener, boolean) * @see #takeAudioCall(Intent, SipAudioCall.Listener, boolean)
*/ */
public SipAudioCall takeAudioCall(Context context, public SipAudioCall takeAudioCall(Intent incomingCallIntent,
Intent incomingCallIntent, SipAudioCall.Listener listener) SipAudioCall.Listener listener) throws SipException {
throws SipException { return takeAudioCall(incomingCallIntent, listener, true);
return takeAudioCall(context, incomingCallIntent, listener, true);
} }
/** /**
@ -298,16 +319,15 @@ public class SipManager {
* {@link SipAudioCall.Listener#onRinging} * {@link SipAudioCall.Listener#onRinging}
* callback. * callback.
* *
* @param context context to create a {@link SipAudioCall} object
* @param incomingCallIntent the incoming call broadcast intent * @param incomingCallIntent the incoming call broadcast intent
* @param listener to listen to the call events from {@link SipAudioCall}; * @param listener to listen to the call events from {@link SipAudioCall};
* can be null * can be null
* @return a {@link SipAudioCall} object * @return a {@link SipAudioCall} object
* @throws SipException if calling the SIP service results in an error * @throws SipException if calling the SIP service results in an error
*/ */
public SipAudioCall takeAudioCall(Context context, public SipAudioCall takeAudioCall(Intent incomingCallIntent,
Intent incomingCallIntent, SipAudioCall.Listener listener, SipAudioCall.Listener listener, boolean ringtoneEnabled)
boolean ringtoneEnabled) throws SipException { throws SipException {
if (incomingCallIntent == null) return null; if (incomingCallIntent == null) return null;
String callId = getCallId(incomingCallIntent); String callId = getCallId(incomingCallIntent);
@ -324,10 +344,10 @@ public class SipManager {
try { try {
ISipSession session = mSipService.getPendingSession(callId); ISipSession session = mSipService.getPendingSession(callId);
if (session == null) return null; if (session == null) return null;
SipAudioCall call = new SipAudioCallImpl( SipAudioCall call = new SipAudioCall(
context, session.getLocalProfile()); mContext, session.getLocalProfile());
call.setRingtoneEnabled(ringtoneEnabled); call.setRingtoneEnabled(ringtoneEnabled);
call.attachCall(session, offerSd); call.attachCall(new SipSession(session), offerSd);
call.setListener(listener); call.setListener(listener);
return call; return call;
} catch (Throwable t) { } catch (Throwable t) {
@ -355,7 +375,7 @@ public class SipManager {
* @return the call ID or null if the intent does not contain it * @return the call ID or null if the intent does not contain it
*/ */
public static String getCallId(Intent incomingCallIntent) { 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 * have it
*/ */
public static String getOfferSessionDescription(Intent incomingCallIntent) { public static String getOfferSessionDescription(Intent incomingCallIntent) {
return incomingCallIntent.getStringExtra(OFFER_SD_KEY); return incomingCallIntent.getStringExtra(EXTRA_OFFER_SD);
} }
/** /**
* Creates an incoming call broadcast intent. * Creates an incoming call broadcast intent.
* *
* @param action the action string to broadcast
* @param callId the call ID of the incoming call * @param callId the call ID of the incoming call
* @param sessionDescription the session description of the incoming call * @param sessionDescription the session description of the incoming call
* @return the incoming call intent * @return the incoming call intent
* @hide * @hide
*/ */
public static Intent createIncomingCallBroadcast(String action, public static Intent createIncomingCallBroadcast(String callId,
String callId, String sessionDescription) { String sessionDescription) {
Intent intent = new Intent(action); Intent intent = new Intent();
intent.putExtra(CALL_ID_KEY, callId); intent.putExtra(EXTRA_CALL_ID, callId);
intent.putExtra(OFFER_SD_KEY, sessionDescription); intent.putExtra(EXTRA_OFFER_SD, sessionDescription);
return intent; return intent;
} }
/** /**
* Registers the profile to the corresponding server for receiving calls. * Manually registers the profile to the corresponding SIP provider for
* {@link #open} is still needed to be called at least once in order for * receiving calls. {@link #open(SipProfile, String, SipRegistrationListener)}
* the SIP service to broadcast an intent when an incoming call is received. * 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 localProfile the SIP profile to register with
* @param expiryTime registration expiration time (in seconds) * @param expiryTime registration expiration time (in seconds)
@ -409,8 +429,10 @@ public class SipManager {
} }
/** /**
* Unregisters the profile from the corresponding server for not receiving * Manually unregisters the profile from the corresponding SIP provider for
* further calls. * 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 localProfile the SIP profile to register with
* @param listener to listen to the registration events * @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 localProfile the SIP profile the session is associated with
* @param listener to listen to SIP session events * @param listener to listen to SIP session events
*/ */
public ISipSession createSipSession(SipProfile localProfile, public SipSession createSipSession(SipProfile localProfile,
ISipSessionListener listener) throws SipException { SipSession.Listener listener) throws SipException {
try { try {
return mSipService.createSession(localProfile, listener); ISipSession s = mSipService.createSession(localProfile, null);
return new SipSession(s, listener);
} catch (RemoteException e) { } catch (RemoteException e) {
throw new SipException("createSipSession()", e); throw new SipException("createSipSession()", e);
} }

View File

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