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:
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
531
voip/java/android/net/sip/SipSession.java
Normal file
531
voip/java/android/net/sip/SipSession.java
Normal 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -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() {
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user