M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
/*
* 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.app.PendingIntent;
import android.net.sip.ISipSession;
import android.net.sip.ISipSessionListener;
import android.net.sip.SipProfile;
/**
* {@hide}
*/
interface ISipService {
void open(in SipProfile localProfile);
void open3(in SipProfile localProfile,
in PendingIntent incomingCallPendingIntent,
in ISipSessionListener listener);
void close(in String localProfileUri);
boolean isOpened(String localProfileUri);
boolean isRegistered(String localProfileUri);
void setRegistrationListener(String localProfileUri,
ISipSessionListener listener);
ISipSession createSession(in SipProfile localProfile,
in ISipSessionListener listener);
ISipSession getPendingSession(String callId);
SipProfile[] getListOfProfiles();
}

View File

@@ -0,0 +1,148 @@
/*
* 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.net.sip.ISipSessionListener;
import android.net.sip.SipProfile;
/**
* A SIP session that is associated with a SIP dialog or a transaction that is
* not within a dialog.
* @hide
*/
interface ISipSession {
/**
* Gets the IP address of the local host on which this SIP session runs.
*
* @return the IP address of the local host
*/
String getLocalIp();
/**
* Gets the SIP profile that this session is associated with.
*
* @return the SIP profile that this session is associated with
*/
SipProfile getLocalProfile();
/**
* 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
*/
SipProfile getPeerProfile();
/**
* Gets the session state. The value returned must be one of the states in
* {@link SipSessionState}.
*
* @return the session state
*/
int getState();
/**
* Checks if the session is in a call.
*
* @return true if the session is in a call
*/
boolean isInCall();
/**
* Gets the call ID of the session.
*
* @return the call ID
*/
String getCallId();
/**
* Sets the listener to listen to the session events. A {@link ISipSession}
* 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
*/
void setListener(in ISipSessionListener 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 ISipSessionListener
*/
void register(int duration);
/**
* 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 ISipSessionListener
*/
void unregister();
/**
* 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
* @see ISipSessionListener
*/
void makeCall(in SipProfile callee, String sessionDescription, int timeout);
/**
* 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
*/
void answerCall(String sessionDescription, int timeout);
/**
* 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}.
*/
void endCall();
/**
* 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
*/
void changeCall(String sessionDescription, int timeout);
}

View File

@@ -0,0 +1,125 @@
/*
* 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.net.sip.ISipSession;
import android.net.sip.SipProfile;
/**
* Listener class to listen to SIP session events.
* @hide
*/
interface ISipSessionListener {
/**
* Called when an INVITE request is sent to initiate a new call.
*
* @param session the session object that carries out the transaction
*/
void onCalling(in ISipSession 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
*/
void onRinging(in ISipSession session, in 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
*/
void onRingingBack(in ISipSession 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
*/
void onCallEstablished(in ISipSession session,
String sessionDescription);
/**
* Called when the session is terminated.
*
* @param session the session object that is associated with the dialog
*/
void onCallEnded(in ISipSession session);
/**
* Called when the peer is busy during session initialization.
*
* @param session the session object that carries out the transaction
*/
void onCallBusy(in ISipSession 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
*/
void onError(in ISipSession 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
*/
void onCallChangeFailed(in ISipSession session, int errorCode,
String errorMessage);
/**
* Called when a registration request is sent.
*
* @param session the session object that carries out the transaction
*/
void onRegistering(in ISipSession 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
*/
void onRegistrationDone(in ISipSession 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
*/
void onRegistrationFailed(in ISipSession session, int errorCode,
String errorMessage);
/**
* Called when the registration gets timed out.
*
* @param session the session object that carries out the transaction
*/
void onRegistrationTimeout(in ISipSession session);
}

View File

@@ -0,0 +1,612 @@
/*
* 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 java.util.ArrayList;
import java.util.Arrays;
/**
* An object used to manipulate messages of Session Description Protocol (SDP).
* It is mainly designed for the uses of Session Initiation Protocol (SIP).
* Therefore, it only handles connection addresses ("c="), bandwidth limits,
* ("b="), encryption keys ("k="), and attribute fields ("a="). Currently this
* implementation does not support multicast sessions.
*
* <p>Here is an example code to create a session description.</p>
* <pre>
* SimpleSessionDescription description = new SimpleSessionDescription(
* System.currentTimeMillis(), "1.2.3.4");
* Media media = description.newMedia("audio", 56789, 1, "RTP/AVP");
* media.setRtpPayload(0, "PCMU/8000", null);
* media.setRtpPayload(8, "PCMA/8000", null);
* media.setRtpPayload(127, "telephone-event/8000", "0-15");
* media.setAttribute("sendrecv", "");
* </pre>
* <p>Invoking <code>description.encode()</code> will produce a result like the
* one below.</p>
* <pre>
* v=0
* o=- 1284970442706 1284970442709 IN IP4 1.2.3.4
* s=-
* c=IN IP4 1.2.3.4
* t=0 0
* m=audio 56789 RTP/AVP 0 8 127
* a=rtpmap:0 PCMU/8000
* a=rtpmap:8 PCMA/8000
* a=rtpmap:127 telephone-event/8000
* a=fmtp:127 0-15
* a=sendrecv
* </pre>
* @hide
*/
public class SimpleSessionDescription {
private final Fields mFields = new Fields("voscbtka");
private final ArrayList<Media> mMedia = new ArrayList<Media>();
/**
* Creates a minimal session description from the given session ID and
* unicast address. The address is used in the origin field ("o=") and the
* connection field ("c="). See {@link SimpleSessionDescription} for an
* example of its usage.
*/
public SimpleSessionDescription(long sessionId, String address) {
address = (address.indexOf(':') < 0 ? "IN IP4 " : "IN IP6 ") + address;
mFields.parse("v=0");
mFields.parse(String.format("o=- %d %d %s", sessionId,
System.currentTimeMillis(), address));
mFields.parse("s=-");
mFields.parse("t=0 0");
mFields.parse("c=" + address);
}
/**
* Creates a session description from the given message.
*
* @throws IllegalArgumentException if message is invalid.
*/
public SimpleSessionDescription(String message) {
String[] lines = message.trim().replaceAll(" +", " ").split("[\r\n]+");
Fields fields = mFields;
for (String line : lines) {
try {
if (line.charAt(1) != '=') {
throw new IllegalArgumentException();
}
if (line.charAt(0) == 'm') {
String[] parts = line.substring(2).split(" ", 4);
String[] ports = parts[1].split("/", 2);
Media media = newMedia(parts[0], Integer.parseInt(ports[0]),
(ports.length < 2) ? 1 : Integer.parseInt(ports[1]),
parts[2]);
for (String format : parts[3].split(" ")) {
media.setFormat(format, null);
}
fields = media;
} else {
fields.parse(line);
}
} catch (Exception e) {
throw new IllegalArgumentException("Invalid SDP: " + line);
}
}
}
/**
* Creates a new media description in this session description.
*
* @param type The media type, e.g. {@code "audio"}.
* @param port The first transport port used by this media.
* @param portCount The number of contiguous ports used by this media.
* @param protocol The transport protocol, e.g. {@code "RTP/AVP"}.
*/
public Media newMedia(String type, int port, int portCount,
String protocol) {
Media media = new Media(type, port, portCount, protocol);
mMedia.add(media);
return media;
}
/**
* Returns all the media descriptions in this session description.
*/
public Media[] getMedia() {
return mMedia.toArray(new Media[mMedia.size()]);
}
/**
* Encodes the session description and all its media descriptions in a
* string. Note that the result might be incomplete if a required field
* has never been added before.
*/
public String encode() {
StringBuilder buffer = new StringBuilder();
mFields.write(buffer);
for (Media media : mMedia) {
media.write(buffer);
}
return buffer.toString();
}
/**
* Returns the connection address or {@code null} if it is not present.
*/
public String getAddress() {
return mFields.getAddress();
}
/**
* Sets the connection address. The field will be removed if the address
* is {@code null}.
*/
public void setAddress(String address) {
mFields.setAddress(address);
}
/**
* Returns the encryption method or {@code null} if it is not present.
*/
public String getEncryptionMethod() {
return mFields.getEncryptionMethod();
}
/**
* Returns the encryption key or {@code null} if it is not present.
*/
public String getEncryptionKey() {
return mFields.getEncryptionKey();
}
/**
* Sets the encryption method and the encryption key. The field will be
* removed if the method is {@code null}.
*/
public void setEncryption(String method, String key) {
mFields.setEncryption(method, key);
}
/**
* Returns the types of the bandwidth limits.
*/
public String[] getBandwidthTypes() {
return mFields.getBandwidthTypes();
}
/**
* Returns the bandwidth limit of the given type or {@code -1} if it is not
* present.
*/
public int getBandwidth(String type) {
return mFields.getBandwidth(type);
}
/**
* Sets the bandwith limit for the given type. The field will be removed if
* the value is negative.
*/
public void setBandwidth(String type, int value) {
mFields.setBandwidth(type, value);
}
/**
* Returns the names of all the attributes.
*/
public String[] getAttributeNames() {
return mFields.getAttributeNames();
}
/**
* Returns the attribute of the given name or {@code null} if it is not
* present.
*/
public String getAttribute(String name) {
return mFields.getAttribute(name);
}
/**
* Sets the attribute for the given name. The field will be removed if
* the value is {@code null}. To set a binary attribute, use an empty
* string as the value.
*/
public void setAttribute(String name, String value) {
mFields.setAttribute(name, value);
}
/**
* This class represents a media description of a session description. It
* can only be created by {@link SimpleSessionDescription#newMedia}. Since
* the syntax is more restricted for RTP based protocols, two sets of access
* methods are implemented. See {@link SimpleSessionDescription} for an
* example of its usage.
*/
public static class Media extends Fields {
private final String mType;
private final int mPort;
private final int mPortCount;
private final String mProtocol;
private ArrayList<String> mFormats = new ArrayList<String>();
private Media(String type, int port, int portCount, String protocol) {
super("icbka");
mType = type;
mPort = port;
mPortCount = portCount;
mProtocol = protocol;
}
/**
* Returns the media type.
*/
public String getType() {
return mType;
}
/**
* Returns the first transport port used by this media.
*/
public int getPort() {
return mPort;
}
/**
* Returns the number of contiguous ports used by this media.
*/
public int getPortCount() {
return mPortCount;
}
/**
* Returns the transport protocol.
*/
public String getProtocol() {
return mProtocol;
}
/**
* Returns the media formats.
*/
public String[] getFormats() {
return mFormats.toArray(new String[mFormats.size()]);
}
/**
* Returns the {@code fmtp} attribute of the given format or
* {@code null} if it is not present.
*/
public String getFmtp(String format) {
return super.get("a=fmtp:" + format, ' ');
}
/**
* Sets a format and its {@code fmtp} attribute. If the attribute is
* {@code null}, the corresponding field will be removed.
*/
public void setFormat(String format, String fmtp) {
mFormats.remove(format);
mFormats.add(format);
super.set("a=rtpmap:" + format, ' ', null);
super.set("a=fmtp:" + format, ' ', fmtp);
}
/**
* Removes a format and its {@code fmtp} attribute.
*/
public void removeFormat(String format) {
mFormats.remove(format);
super.set("a=rtpmap:" + format, ' ', null);
super.set("a=fmtp:" + format, ' ', null);
}
/**
* Returns the RTP payload types.
*/
public int[] getRtpPayloadTypes() {
int[] types = new int[mFormats.size()];
int length = 0;
for (String format : mFormats) {
try {
types[length] = Integer.parseInt(format);
++length;
} catch (NumberFormatException e) { }
}
return Arrays.copyOf(types, length);
}
/**
* Returns the {@code rtpmap} attribute of the given RTP payload type
* or {@code null} if it is not present.
*/
public String getRtpmap(int type) {
return super.get("a=rtpmap:" + type, ' ');
}
/**
* Returns the {@code fmtp} attribute of the given RTP payload type or
* {@code null} if it is not present.
*/
public String getFmtp(int type) {
return super.get("a=fmtp:" + type, ' ');
}
/**
* Sets a RTP payload type and its {@code rtpmap} and {@code fmtp}
* attributes. If any of the attributes is {@code null}, the
* corresponding field will be removed. See
* {@link SimpleSessionDescription} for an example of its usage.
*/
public void setRtpPayload(int type, String rtpmap, String fmtp) {
String format = String.valueOf(type);
mFormats.remove(format);
mFormats.add(format);
super.set("a=rtpmap:" + format, ' ', rtpmap);
super.set("a=fmtp:" + format, ' ', fmtp);
}
/**
* Removes a RTP payload and its {@code rtpmap} and {@code fmtp}
* attributes.
*/
public void removeRtpPayload(int type) {
removeFormat(String.valueOf(type));
}
private void write(StringBuilder buffer) {
buffer.append("m=").append(mType).append(' ').append(mPort);
if (mPortCount != 1) {
buffer.append('/').append(mPortCount);
}
buffer.append(' ').append(mProtocol);
for (String format : mFormats) {
buffer.append(' ').append(format);
}
buffer.append("\r\n");
super.write(buffer);
}
}
/**
* This class acts as a set of fields, and the size of the set is expected
* to be small. Therefore, it uses a simple list instead of maps. Each field
* has three parts: a key, a delimiter, and a value. Delimiters are special
* because they are not included in binary attributes. As a result, the
* private methods, which are the building blocks of this class, all take
* the delimiter as an argument.
*/
private static class Fields {
private final String mOrder;
private final ArrayList<String> mLines = new ArrayList<String>();
Fields(String order) {
mOrder = order;
}
/**
* Returns the connection address or {@code null} if it is not present.
*/
public String getAddress() {
String address = get("c", '=');
if (address == null) {
return null;
}
String[] parts = address.split(" ");
if (parts.length != 3) {
return null;
}
int slash = parts[2].indexOf('/');
return (slash < 0) ? parts[2] : parts[2].substring(0, slash);
}
/**
* Sets the connection address. The field will be removed if the address
* is {@code null}.
*/
public void setAddress(String address) {
if (address != null) {
address = (address.indexOf(':') < 0 ? "IN IP4 " : "IN IP6 ") +
address;
}
set("c", '=', address);
}
/**
* Returns the encryption method or {@code null} if it is not present.
*/
public String getEncryptionMethod() {
String encryption = get("k", '=');
if (encryption == null) {
return null;
}
int colon = encryption.indexOf(':');
return (colon == -1) ? encryption : encryption.substring(0, colon);
}
/**
* Returns the encryption key or {@code null} if it is not present.
*/
public String getEncryptionKey() {
String encryption = get("k", '=');
if (encryption == null) {
return null;
}
int colon = encryption.indexOf(':');
return (colon == -1) ? null : encryption.substring(0, colon + 1);
}
/**
* Sets the encryption method and the encryption key. The field will be
* removed if the method is {@code null}.
*/
public void setEncryption(String method, String key) {
set("k", '=', (method == null || key == null) ?
method : method + ':' + key);
}
/**
* Returns the types of the bandwidth limits.
*/
public String[] getBandwidthTypes() {
return cut("b=", ':');
}
/**
* Returns the bandwidth limit of the given type or {@code -1} if it is
* not present.
*/
public int getBandwidth(String type) {
String value = get("b=" + type, ':');
if (value != null) {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) { }
setBandwidth(type, -1);
}
return -1;
}
/**
* Sets the bandwith limit for the given type. The field will be removed
* if the value is negative.
*/
public void setBandwidth(String type, int value) {
set("b=" + type, ':', (value < 0) ? null : String.valueOf(value));
}
/**
* Returns the names of all the attributes.
*/
public String[] getAttributeNames() {
return cut("a=", ':');
}
/**
* Returns the attribute of the given name or {@code null} if it is not
* present.
*/
public String getAttribute(String name) {
return get("a=" + name, ':');
}
/**
* Sets the attribute for the given name. The field will be removed if
* the value is {@code null}. To set a binary attribute, use an empty
* string as the value.
*/
public void setAttribute(String name, String value) {
set("a=" + name, ':', value);
}
private void write(StringBuilder buffer) {
for (int i = 0; i < mOrder.length(); ++i) {
char type = mOrder.charAt(i);
for (String line : mLines) {
if (line.charAt(0) == type) {
buffer.append(line).append("\r\n");
}
}
}
}
/**
* Invokes {@link #set} after splitting the line into three parts.
*/
private void parse(String line) {
char type = line.charAt(0);
if (mOrder.indexOf(type) == -1) {
return;
}
char delimiter = '=';
if (line.startsWith("a=rtpmap:") || line.startsWith("a=fmtp:")) {
delimiter = ' ';
} else if (type == 'b' || type == 'a') {
delimiter = ':';
}
int i = line.indexOf(delimiter);
if (i == -1) {
set(line, delimiter, "");
} else {
set(line.substring(0, i), delimiter, line.substring(i + 1));
}
}
/**
* Finds the key with the given prefix and returns its suffix.
*/
private String[] cut(String prefix, char delimiter) {
String[] names = new String[mLines.size()];
int length = 0;
for (String line : mLines) {
if (line.startsWith(prefix)) {
int i = line.indexOf(delimiter);
if (i == -1) {
i = line.length();
}
names[length] = line.substring(prefix.length(), i);
++length;
}
}
return Arrays.copyOf(names, length);
}
/**
* Returns the index of the key.
*/
private int find(String key, char delimiter) {
int length = key.length();
for (int i = mLines.size() - 1; i >= 0; --i) {
String line = mLines.get(i);
if (line.startsWith(key) && (line.length() == length ||
line.charAt(length) == delimiter)) {
return i;
}
}
return -1;
}
/**
* Sets the key with the value or removes the key if the value is
* {@code null}.
*/
private void set(String key, char delimiter, String value) {
int index = find(key, delimiter);
if (value != null) {
if (value.length() != 0) {
key = key + delimiter + value;
}
if (index == -1) {
mLines.add(key);
} else {
mLines.set(index, key);
}
} else if (index != -1) {
mLines.remove(index);
}
}
/**
* Returns the value of the key.
*/
private String get(String key, char delimiter) {
int index = find(key, delimiter);
if (index == -1) {
return null;
}
String line = mLines.get(index);
int length = key.length();
return (line.length() == length) ? "" : line.substring(length + 1);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,101 @@
/*
* 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 error codes returned during SIP actions. For example, during
* {@link SipRegistrationListener#onRegistrationFailed onRegistrationFailed()},
* {@link SipSession.Listener#onError onError()},
* {@link SipSession.Listener#onCallChangeFailed onCallChangeFailed()} and
* {@link SipSession.Listener#onRegistrationFailed onRegistrationFailed()}.
*/
public class SipErrorCode {
/** Not an error. */
public static final int NO_ERROR = 0;
/** When some socket error occurs. */
public static final int SOCKET_ERROR = -1;
/** When server responds with an error. */
public static final int SERVER_ERROR = -2;
/** When transaction is terminated unexpectedly. */
public static final int TRANSACTION_TERMINTED = -3;
/** When some error occurs on the device, possibly due to a bug. */
public static final int CLIENT_ERROR = -4;
/** When the transaction gets timed out. */
public static final int TIME_OUT = -5;
/** When the remote URI is not valid. */
public static final int INVALID_REMOTE_URI = -6;
/** When the peer is not reachable. */
public static final int PEER_NOT_REACHABLE = -7;
/** When invalid credentials are provided. */
public static final int INVALID_CREDENTIALS = -8;
/** The client is in a transaction and cannot initiate a new one. */
public static final int IN_PROGRESS = -9;
/** When data connection is lost. */
public static final int DATA_CONNECTION_LOST = -10;
/** Cross-domain authentication required. */
public static final int CROSS_DOMAIN_AUTHENTICATION = -11;
/** When the server is not reachable. */
public static final int SERVER_UNREACHABLE = -12;
public static String toString(int errorCode) {
switch (errorCode) {
case NO_ERROR:
return "NO_ERROR";
case SOCKET_ERROR:
return "SOCKET_ERROR";
case SERVER_ERROR:
return "SERVER_ERROR";
case TRANSACTION_TERMINTED:
return "TRANSACTION_TERMINTED";
case CLIENT_ERROR:
return "CLIENT_ERROR";
case TIME_OUT:
return "TIME_OUT";
case INVALID_REMOTE_URI:
return "INVALID_REMOTE_URI";
case PEER_NOT_REACHABLE:
return "PEER_NOT_REACHABLE";
case INVALID_CREDENTIALS:
return "INVALID_CREDENTIALS";
case IN_PROGRESS:
return "IN_PROGRESS";
case DATA_CONNECTION_LOST:
return "DATA_CONNECTION_LOST";
case CROSS_DOMAIN_AUTHENTICATION:
return "CROSS_DOMAIN_AUTHENTICATION";
case SERVER_UNREACHABLE:
return "SERVER_UNREACHABLE";
default:
return "UNKNOWN";
}
}
private SipErrorCode() {
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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;
/**
* Indicates a general SIP-related exception.
*/
public class SipException extends Exception {
public SipException() {
}
public SipException(String message) {
super(message);
}
public SipException(String message, Throwable cause) {
// we want to eliminate the dependency on javax.sip.SipException
super(message, ((cause instanceof javax.sip.SipException)
&& (cause.getCause() != null))
? cause.getCause()
: cause);
}
}

View File

@@ -0,0 +1,608 @@
/*
* 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.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import java.text.ParseException;
/**
* Provides APIs for SIP tasks, such as initiating SIP connections, and provides access to related
* SIP services. This class is the starting point for any SIP actions. You can acquire an instance
* of it with {@link #newInstance newInstance()}.</p>
* <p>The APIs in this class allows you to:</p>
* <ul>
* <li>Create a {@link SipSession} to get ready for making calls or listen for incoming calls. See
* {@link #createSipSession createSipSession()} and {@link #getSessionFor getSessionFor()}.</li>
* <li>Initiate and receive generic SIP calls or audio-only SIP calls. Generic SIP calls may
* be video, audio, or other, and are initiated with {@link #open open()}. Audio-only SIP calls
* should be handled with a {@link SipAudioCall}, which you can acquire with {@link
* #makeAudioCall makeAudioCall()} and {@link #takeAudioCall takeAudioCall()}.</li>
* <li>Register and unregister with a SIP service provider, with
* {@link #register register()} and {@link #unregister unregister()}.</li>
* <li>Verify session connectivity, with {@link #isOpened isOpened()} and
* {@link #isRegistered isRegistered()}.</li>
* </ul>
* <p class="note"><strong>Note:</strong> Not all Android-powered devices support VOIP calls using
* SIP. You should always call {@link android.net.sip.SipManager#isVoipSupported
* isVoipSupported()} to verify that the device supports VOIP calling and {@link
* android.net.sip.SipManager#isApiSupported isApiSupported()} to verify that the device supports
* the SIP APIs.<br/><br/>Your application must also request the {@link
* android.Manifest.permission#INTERNET} and {@link android.Manifest.permission#USE_SIP}
* permissions.</p>
*/
public class SipManager {
/**
* The result code to be sent back with the incoming call
* {@link PendingIntent}.
* @see #open(SipProfile, PendingIntent, SipRegistrationListener)
*/
public static final int INCOMING_CALL_RESULT_CODE = 101;
/**
* Key to retrieve the call ID from an incoming call intent.
* @see #open(SipProfile, PendingIntent, SipRegistrationListener)
*/
public static final String EXTRA_CALL_ID = "android:sipCallID";
/**
* Key to retrieve the offered session description from an incoming call
* intent.
* @see #open(SipProfile, PendingIntent, SipRegistrationListener)
*/
public static final String EXTRA_OFFER_SD = "android:sipOfferSD";
/**
* Action to broadcast when SipService is up.
* Internal use only.
* @hide
*/
public static final String ACTION_SIP_SERVICE_UP =
"android.net.sip.SIP_SERVICE_UP";
/**
* 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";
/**
* 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";
/**
* 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";
/**
* 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 TAG = "SipManager";
private ISipService mSipService;
private Context mContext;
/**
* Creates a manager instance. Returns null if SIP API is not 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 newInstance(Context context) {
return (isApiSupported(context) ? new SipManager(context) : null);
}
/**
* Returns true if the SIP API is supported by the system.
*/
public static boolean isApiSupported(Context context) {
return context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SIP);
}
/**
* Returns true if the system supports SIP-based VOIP API.
*/
public static boolean isVoipSupported(Context context) {
return context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SIP_VOIP) && isApiSupported(context);
}
/**
* Returns true if SIP is only available on WIFI.
*/
public static boolean isSipWifiOnly(Context context) {
return context.getResources().getBoolean(
com.android.internal.R.bool.config_sip_wifi_only);
}
private SipManager(Context context) {
mContext = context;
createSipService();
}
private void createSipService() {
IBinder b = ServiceManager.getService(Context.SIP_SERVICE);
mSipService = ISipService.Stub.asInterface(b);
}
/**
* Opens the profile for making generic SIP calls. The caller may make subsequent calls
* through {@link #makeAudioCall}. If one also wants to receive calls on the
* profile, use
* {@link #open(SipProfile, PendingIntent, SipRegistrationListener)}
* instead.
*
* @param localProfile the SIP profile to make calls from
* @throws SipException if the profile contains incorrect settings or
* calling the SIP service results in an error
*/
public void open(SipProfile localProfile) throws SipException {
try {
mSipService.open(localProfile);
} catch (RemoteException e) {
throw new SipException("open()", e);
}
}
/**
* Opens the profile for making calls and/or receiving generic SIP 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. When the SIP service
* receives a new call, it will send out an intent with the provided action
* string. The intent contains a call ID extra and an offer session
* description string extra. Use {@link #getCallId} and
* {@link #getOfferSessionDescription} to retrieve those extras.
*
* @param localProfile the SIP profile to receive incoming calls for
* @param incomingCallPendingIntent When an incoming call is received, the
* SIP service will call
* {@link PendingIntent#send(Context, int, Intent)} to send back the
* intent to the caller with {@link #INCOMING_CALL_RESULT_CODE} as the
* result code and the intent to fill in the call ID and session
* description information. It cannot be null.
* @param listener to listen to registration events; can be null
* @see #getCallId
* @see #getOfferSessionDescription
* @see #takeAudioCall
* @throws NullPointerException if {@code incomingCallPendingIntent} is null
* @throws SipException if the profile contains incorrect settings or
* calling the SIP service results in an error
* @see #isIncomingCallIntent
* @see #getCallId
* @see #getOfferSessionDescription
*/
public void open(SipProfile localProfile,
PendingIntent incomingCallPendingIntent,
SipRegistrationListener listener) throws SipException {
if (incomingCallPendingIntent == null) {
throw new NullPointerException(
"incomingCallPendingIntent cannot be null");
}
try {
mSipService.open3(localProfile, incomingCallPendingIntent,
createRelay(listener, localProfile.getUriString()));
} catch (RemoteException e) {
throw new SipException("open()", e);
}
}
/**
* Sets the listener to listen to registration events. No effect if the
* profile has not been opened to receive calls (see
* {@link #open(SipProfile, PendingIntent, SipRegistrationListener)}).
*
* @param localProfileUri the URI of the profile
* @param listener to listen to registration events; can be null
* @throws SipException if calling the SIP service results in an error
*/
public void setRegistrationListener(String localProfileUri,
SipRegistrationListener listener) throws SipException {
try {
mSipService.setRegistrationListener(
localProfileUri, createRelay(listener, localProfileUri));
} catch (RemoteException e) {
throw new SipException("setRegistrationListener()", e);
}
}
/**
* Closes the specified profile to not make/receive calls. All the resources
* that were allocated to the profile are also released.
*
* @param localProfileUri the URI of the profile to close
* @throws SipException if calling the SIP service results in an error
*/
public void close(String localProfileUri) throws SipException {
try {
mSipService.close(localProfileUri);
} catch (RemoteException e) {
throw new SipException("close()", e);
}
}
/**
* 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
* @throws SipException if calling the SIP service results in an error
*/
public boolean isOpened(String localProfileUri) throws SipException {
try {
return mSipService.isOpened(localProfileUri);
} catch (RemoteException e) {
throw new SipException("isOpened()", e);
}
}
/**
* 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 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 {
try {
return mSipService.isRegistered(localProfileUri);
} catch (RemoteException e) {
throw new SipException("isRegistered()", e);
}
}
/**
* Creates a {@link SipAudioCall} to make a call. The attempt will be timed
* out if the call is not established within {@code timeout} seconds and
* {@link SipAudioCall.Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
* will be called.
*
* @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};
* can be null
* @param timeout the timeout value in seconds. Default value (defined by
* SIP protocol) is used if {@code timeout} is zero or negative.
* @return a {@link SipAudioCall} object
* @throws SipException if calling the SIP service results in an error or
* VOIP API is not supported by the device
* @see SipAudioCall.Listener#onError
* @see #isVoipSupported
*/
public SipAudioCall makeAudioCall(SipProfile localProfile,
SipProfile peerProfile, SipAudioCall.Listener listener, int timeout)
throws SipException {
if (!isVoipSupported(mContext)) {
throw new SipException("VOIP API is not supported");
}
SipAudioCall call = new SipAudioCall(mContext, localProfile);
call.setListener(listener);
SipSession s = createSipSession(localProfile, null);
call.makeCall(peerProfile, s, timeout);
return call;
}
/**
* Creates a {@link SipAudioCall} to make an audio call. The attempt will be
* timed out if the call is not established within {@code timeout} seconds
* and
* {@link SipAudioCall.Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
* will be called.
*
* @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};
* can be null
* @param timeout the timeout value in seconds. Default value (defined by
* SIP protocol) is used if {@code timeout} is zero or negative.
* @return a {@link SipAudioCall} object
* @throws SipException if calling the SIP service results in an error or
* VOIP API is not supported by the device
* @see SipAudioCall.Listener#onError
* @see #isVoipSupported
*/
public SipAudioCall makeAudioCall(String localProfileUri,
String peerProfileUri, SipAudioCall.Listener listener, int timeout)
throws SipException {
if (!isVoipSupported(mContext)) {
throw new SipException("VOIP API is not supported");
}
try {
return makeAudioCall(
new SipProfile.Builder(localProfileUri).build(),
new SipProfile.Builder(peerProfileUri).build(), listener,
timeout);
} catch (ParseException e) {
throw new SipException("build SipProfile", e);
}
}
/**
* Creates a {@link SipAudioCall} to take an incoming call. Before the call
* is returned, the listener will receive a
* {@link SipAudioCall.Listener#onRinging}
* callback.
*
* @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(Intent incomingCallIntent,
SipAudioCall.Listener listener) throws SipException {
if (incomingCallIntent == null) {
throw new SipException("Cannot retrieve session with null intent");
}
String callId = getCallId(incomingCallIntent);
if (callId == null) {
throw new SipException("Call ID missing in incoming call intent");
}
String offerSd = getOfferSessionDescription(incomingCallIntent);
if (offerSd == null) {
throw new SipException("Session description missing in incoming "
+ "call intent");
}
try {
ISipSession session = mSipService.getPendingSession(callId);
if (session == null) {
throw new SipException("No pending session for the call");
}
SipAudioCall call = new SipAudioCall(
mContext, session.getLocalProfile());
call.attachCall(new SipSession(session), offerSd);
call.setListener(listener);
return call;
} catch (Throwable t) {
throw new SipException("takeAudioCall()", t);
}
}
/**
* Checks if the intent is an incoming call broadcast intent.
*
* @param intent the intent in question
* @return true if the intent is an incoming call broadcast intent
*/
public static boolean isIncomingCallIntent(Intent intent) {
if (intent == null) return false;
String callId = getCallId(intent);
String offerSd = getOfferSessionDescription(intent);
return ((callId != null) && (offerSd != null));
}
/**
* Gets the call ID from the specified incoming call broadcast intent.
*
* @param incomingCallIntent the incoming call broadcast intent
* @return the call ID or null if the intent does not contain it
*/
public static String getCallId(Intent incomingCallIntent) {
return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
}
/**
* Gets the offer session description from the specified incoming call
* broadcast intent.
*
* @param incomingCallIntent the incoming call broadcast intent
* @return the offer session description or null if the intent does not
* have it
*/
public static String getOfferSessionDescription(Intent incomingCallIntent) {
return incomingCallIntent.getStringExtra(EXTRA_OFFER_SD);
}
/**
* Creates an incoming call broadcast intent.
*
* @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 callId,
String sessionDescription) {
Intent intent = new Intent();
intent.putExtra(EXTRA_CALL_ID, callId);
intent.putExtra(EXTRA_OFFER_SD, sessionDescription);
return intent;
}
/**
* Manually registers the profile to the corresponding SIP provider for
* receiving calls.
* {@link #open(SipProfile, PendingIntent, SipRegistrationListener)} is
* still needed to be called at least once in order for the SIP service to
* notify the caller with the {@link android.app.PendingIntent} when an incoming call is
* received.
*
* @param localProfile the SIP profile to register with
* @param expiryTime registration expiration time (in seconds)
* @param listener to listen to the registration events
* @throws SipException if calling the SIP service results in an error
*/
public void register(SipProfile localProfile, int expiryTime,
SipRegistrationListener listener) throws SipException {
try {
ISipSession session = mSipService.createSession(localProfile,
createRelay(listener, localProfile.getUriString()));
session.register(expiryTime);
} catch (RemoteException e) {
throw new SipException("register()", e);
}
}
/**
* 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
* @throws SipException if calling the SIP service results in an error
*/
public void unregister(SipProfile localProfile,
SipRegistrationListener listener) throws SipException {
try {
ISipSession session = mSipService.createSession(localProfile,
createRelay(listener, localProfile.getUriString()));
session.unregister();
} catch (RemoteException e) {
throw new SipException("unregister()", e);
}
}
/**
* Gets the {@link SipSession} that handles the incoming call. For audio
* calls, consider to use {@link SipAudioCall} to handle the incoming call.
* See {@link #takeAudioCall}. Note that the method may be called only once
* for the same intent. For subsequent calls on the same intent, the method
* returns null.
*
* @param incomingCallIntent the incoming call broadcast intent
* @return the session object that handles the incoming call
*/
public SipSession getSessionFor(Intent incomingCallIntent)
throws SipException {
try {
String callId = getCallId(incomingCallIntent);
ISipSession s = mSipService.getPendingSession(callId);
return new SipSession(s);
} catch (RemoteException e) {
throw new SipException("getSessionFor()", e);
}
}
private static ISipSessionListener createRelay(
SipRegistrationListener listener, String uri) {
return ((listener == null) ? null : new ListenerRelay(listener, uri));
}
/**
* Creates a {@link SipSession} with the specified profile. Use other
* methods, if applicable, instead of interacting with {@link SipSession}
* directly.
*
* @param localProfile the SIP profile the session is associated with
* @param listener to listen to SIP session events
*/
public SipSession createSipSession(SipProfile localProfile,
SipSession.Listener listener) throws SipException {
try {
ISipSession s = mSipService.createSession(localProfile, null);
if (s == null) {
throw new SipException(
"Failed to create SipSession; network unavailable?");
}
return new SipSession(s, listener);
} catch (RemoteException e) {
throw new SipException("createSipSession()", e);
}
}
/**
* Gets the list of profiles hosted by the SIP service. The user information
* (username, password and display name) are crossed out.
* @hide
*/
public SipProfile[] getListOfProfiles() {
try {
return mSipService.getListOfProfiles();
} catch (RemoteException e) {
return new SipProfile[0];
}
}
private static class ListenerRelay extends SipSessionAdapter {
private SipRegistrationListener mListener;
private String mUri;
// listener must not be null
public ListenerRelay(SipRegistrationListener listener, String uri) {
mListener = listener;
mUri = uri;
}
private String getUri(ISipSession session) {
try {
return ((session == null)
? mUri
: session.getLocalProfile().getUriString());
} catch (Throwable e) {
// SipService died? SIP stack died?
Log.w(TAG, "getUri(): " + e);
return null;
}
}
@Override
public void onRegistering(ISipSession session) {
mListener.onRegistering(getUri(session));
}
@Override
public void onRegistrationDone(ISipSession session, int duration) {
long expiryTime = duration;
if (duration > 0) expiryTime += System.currentTimeMillis();
mListener.onRegistrationDone(getUri(session), expiryTime);
}
@Override
public void onRegistrationFailed(ISipSession session, int errorCode,
String message) {
mListener.onRegistrationFailed(getUri(session), errorCode, message);
}
@Override
public void onRegistrationTimeout(ISipSession session) {
mListener.onRegistrationFailed(getUri(session),
SipErrorCode.TIME_OUT, "registration timed out");
}
}
}

View File

@@ -0,0 +1,19 @@
/*
* 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;
parcelable SipProfile;

View File

@@ -0,0 +1,496 @@
/*
* 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.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.text.ParseException;
import javax.sip.InvalidArgumentException;
import javax.sip.ListeningPoint;
import javax.sip.PeerUnavailableException;
import javax.sip.SipFactory;
import javax.sip.address.Address;
import javax.sip.address.AddressFactory;
import javax.sip.address.SipURI;
import javax.sip.address.URI;
/**
* Defines a SIP profile, including a SIP account, domain and server information.
* <p>You can create a {@link SipProfile} using {@link
* SipProfile.Builder}. You can also retrieve one from a {@link SipSession}, using {@link
* SipSession#getLocalProfile} and {@link SipSession#getPeerProfile}.</p>
*/
public class SipProfile implements Parcelable, Serializable, Cloneable {
private static final long serialVersionUID = 1L;
private static final int DEFAULT_PORT = 5060;
private static final String TCP = "TCP";
private static final String UDP = "UDP";
private Address mAddress;
private String mProxyAddress;
private String mPassword;
private String mDomain;
private String mProtocol = UDP;
private String mProfileName;
private String mAuthUserName;
private int mPort = DEFAULT_PORT;
private boolean mSendKeepAlive = false;
private boolean mAutoRegistration = true;
private transient int mCallingUid = 0;
public static final Parcelable.Creator<SipProfile> CREATOR =
new Parcelable.Creator<SipProfile>() {
public SipProfile createFromParcel(Parcel in) {
return new SipProfile(in);
}
public SipProfile[] newArray(int size) {
return new SipProfile[size];
}
};
/**
* Helper class for creating a {@link SipProfile}.
*/
public static class Builder {
private AddressFactory mAddressFactory;
private SipProfile mProfile = new SipProfile();
private SipURI mUri;
private String mDisplayName;
private String mProxyAddress;
{
try {
mAddressFactory =
SipFactory.getInstance().createAddressFactory();
} catch (PeerUnavailableException e) {
throw new RuntimeException(e);
}
}
/**
* Creates a builder based on the given profile.
*/
public Builder(SipProfile profile) {
if (profile == null) throw new NullPointerException();
try {
mProfile = (SipProfile) profile.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("should not occur", e);
}
mProfile.mAddress = null;
mUri = profile.getUri();
mUri.setUserPassword(profile.getPassword());
mDisplayName = profile.getDisplayName();
mProxyAddress = profile.getProxyAddress();
mProfile.mPort = profile.getPort();
}
/**
* Constructor.
*
* @param uriString the URI string as "sip:<user_name>@<domain>"
* @throws ParseException if the string is not a valid URI
*/
public Builder(String uriString) throws ParseException {
if (uriString == null) {
throw new NullPointerException("uriString cannot be null");
}
URI uri = mAddressFactory.createURI(fix(uriString));
if (uri instanceof SipURI) {
mUri = (SipURI) uri;
} else {
throw new ParseException(uriString + " is not a SIP URI", 0);
}
mProfile.mDomain = mUri.getHost();
}
/**
* Constructor.
*
* @param username username of the SIP account
* @param serverDomain the SIP server domain; if the network address
* is different from the domain, use {@link #setOutboundProxy} to
* set server address
* @throws ParseException if the parameters are not valid
*/
public Builder(String username, String serverDomain)
throws ParseException {
if ((username == null) || (serverDomain == null)) {
throw new NullPointerException(
"username and serverDomain cannot be null");
}
mUri = mAddressFactory.createSipURI(username, serverDomain);
mProfile.mDomain = serverDomain;
}
private String fix(String uriString) {
return (uriString.trim().toLowerCase().startsWith("sip:")
? uriString
: "sip:" + uriString);
}
/**
* Sets the username used for authentication.
*
* @param name auth. name of the profile
* @return this builder object
* @hide // TODO: remove when we make it public
*/
public Builder setAuthUserName(String name) {
mProfile.mAuthUserName = name;
return this;
}
/**
* Sets the name of the profile. This name is given by user.
*
* @param name name of the profile
* @return this builder object
*/
public Builder setProfileName(String name) {
mProfile.mProfileName = name;
return this;
}
/**
* Sets the password of the SIP account
*
* @param password password of the SIP account
* @return this builder object
*/
public Builder setPassword(String password) {
mUri.setUserPassword(password);
return this;
}
/**
* Sets the port number of the server. By default, it is 5060.
*
* @param port port number of the server
* @return this builder object
* @throws IllegalArgumentException if the port number is out of range
*/
public Builder setPort(int port) throws IllegalArgumentException {
if ((port > 65535) || (port < 1000)) {
throw new IllegalArgumentException("incorrect port arugment: " + port);
}
mProfile.mPort = port;
return this;
}
/**
* Sets the protocol used to connect to the SIP server. Currently,
* only "UDP" and "TCP" are supported.
*
* @param protocol the protocol string
* @return this builder object
* @throws IllegalArgumentException if the protocol is not recognized
*/
public Builder setProtocol(String protocol)
throws IllegalArgumentException {
if (protocol == null) {
throw new NullPointerException("protocol cannot be null");
}
protocol = protocol.toUpperCase();
if (!protocol.equals(UDP) && !protocol.equals(TCP)) {
throw new IllegalArgumentException(
"unsupported protocol: " + protocol);
}
mProfile.mProtocol = protocol;
return this;
}
/**
* Sets the outbound proxy of the SIP server.
*
* @param outboundProxy the network address of the outbound proxy
* @return this builder object
*/
public Builder setOutboundProxy(String outboundProxy) {
mProxyAddress = outboundProxy;
return this;
}
/**
* Sets the display name of the user.
*
* @param displayName display name of the user
* @return this builder object
*/
public Builder setDisplayName(String displayName) {
mDisplayName = displayName;
return this;
}
/**
* Sets the send keep-alive flag.
*
* @param flag true if sending keep-alive message is required,
* false otherwise
* @return this builder object
*/
public Builder setSendKeepAlive(boolean flag) {
mProfile.mSendKeepAlive = flag;
return this;
}
/**
* Sets the auto. registration flag.
*
* @param flag true if the profile will be registered automatically,
* false otherwise
* @return this builder object
*/
public Builder setAutoRegistration(boolean flag) {
mProfile.mAutoRegistration = flag;
return this;
}
/**
* Builds and returns the SIP profile object.
*
* @return the profile object created
*/
public SipProfile build() {
// remove password from URI
mProfile.mPassword = mUri.getUserPassword();
mUri.setUserPassword(null);
try {
if (!TextUtils.isEmpty(mProxyAddress)) {
SipURI uri = (SipURI)
mAddressFactory.createURI(fix(mProxyAddress));
mProfile.mProxyAddress = uri.getHost();
} else {
if (!mProfile.mProtocol.equals(UDP)) {
mUri.setTransportParam(mProfile.mProtocol);
}
if (mProfile.mPort != DEFAULT_PORT) {
mUri.setPort(mProfile.mPort);
}
}
mProfile.mAddress = mAddressFactory.createAddress(
mDisplayName, mUri);
} catch (InvalidArgumentException e) {
throw new RuntimeException(e);
} catch (ParseException e) {
// must not occur
throw new RuntimeException(e);
}
return mProfile;
}
}
private SipProfile() {
}
private SipProfile(Parcel in) {
mAddress = (Address) in.readSerializable();
mProxyAddress = in.readString();
mPassword = in.readString();
mDomain = in.readString();
mProtocol = in.readString();
mProfileName = in.readString();
mSendKeepAlive = (in.readInt() == 0) ? false : true;
mAutoRegistration = (in.readInt() == 0) ? false : true;
mCallingUid = in.readInt();
mPort = in.readInt();
mAuthUserName = in.readString();
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeSerializable(mAddress);
out.writeString(mProxyAddress);
out.writeString(mPassword);
out.writeString(mDomain);
out.writeString(mProtocol);
out.writeString(mProfileName);
out.writeInt(mSendKeepAlive ? 1 : 0);
out.writeInt(mAutoRegistration ? 1 : 0);
out.writeInt(mCallingUid);
out.writeInt(mPort);
out.writeString(mAuthUserName);
}
@Override
public int describeContents() {
return 0;
}
/**
* Gets the SIP URI of this profile.
*
* @return the SIP URI of this profile
* @hide
*/
public SipURI getUri() {
return (SipURI) mAddress.getURI();
}
/**
* Gets the SIP URI string of this profile.
*
* @return the SIP URI string of this profile
*/
public String getUriString() {
// We need to return the sip uri domain instead of
// the SIP URI with transport, port information if
// the outbound proxy address exists.
if (!TextUtils.isEmpty(mProxyAddress)) {
return "sip:" + getUserName() + "@" + mDomain;
}
return getUri().toString();
}
/**
* Gets the SIP address of this profile.
*
* @return the SIP address of this profile
* @hide
*/
public Address getSipAddress() {
return mAddress;
}
/**
* Gets the display name of the user.
*
* @return the display name of the user
*/
public String getDisplayName() {
return mAddress.getDisplayName();
}
/**
* Gets the username.
*
* @return the username
*/
public String getUserName() {
return getUri().getUser();
}
/**
* Gets the username for authentication. If it is null, then the username
* should be used in authentication instead.
*
* @return the auth. username
* @hide // TODO: remove when we make it public
*/
public String getAuthUserName() {
return mAuthUserName;
}
/**
* Gets the password.
*
* @return the password
*/
public String getPassword() {
return mPassword;
}
/**
* Gets the SIP domain.
*
* @return the SIP domain
*/
public String getSipDomain() {
return mDomain;
}
/**
* Gets the port number of the SIP server.
*
* @return the port number of the SIP server
*/
public int getPort() {
return mPort;
}
/**
* Gets the protocol used to connect to the server.
*
* @return the protocol
*/
public String getProtocol() {
return mProtocol;
}
/**
* Gets the network address of the server outbound proxy.
*
* @return the network address of the server outbound proxy
*/
public String getProxyAddress() {
return mProxyAddress;
}
/**
* Gets the (user-defined) name of the profile.
*
* @return name of the profile
*/
public String getProfileName() {
return mProfileName;
}
/**
* Gets the flag of 'Sending keep-alive'.
*
* @return the flag of sending SIP keep-alive messages.
*/
public boolean getSendKeepAlive() {
return mSendKeepAlive;
}
/**
* Gets the flag of 'Auto Registration'.
*
* @return the flag of registering the profile automatically.
*/
public boolean getAutoRegistration() {
return mAutoRegistration;
}
/**
* Sets the calling process's Uid in the sip service.
* @hide
*/
public void setCallingUid(int uid) {
mCallingUid = uid;
}
/**
* Gets the calling process's Uid in the sip settings.
* @hide
*/
public int getCallingUid() {
return mCallingUid;
}
private Object readResolve() throws ObjectStreamException {
// For compatibility.
if (mPort == 0) mPort = DEFAULT_PORT;
return this;
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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;
/**
* Listener for SIP registration events.
*/
public interface SipRegistrationListener {
/**
* Called when a registration request is sent.
*
* @param localProfileUri the URI string of the SIP profile to register with
*/
void onRegistering(String localProfileUri);
/**
* Called when the registration succeeded.
*
* @param localProfileUri the URI string of the SIP profile to register with
* @param expiryTime duration in seconds before the registration expires
*/
void onRegistrationDone(String localProfileUri, long expiryTime);
/**
* Called when the registration failed.
*
* @param localProfileUri the URI string of the SIP profile to register with
* @param errorCode error code of this error
* @param errorMessage error message
* @see SipErrorCode
*/
void onRegistrationFailed(String localProfileUri, int errorCode,
String errorMessage);
}

View File

@@ -0,0 +1,533 @@
/*
* 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;
/**
* Represents a SIP session that is associated with a SIP dialog or a standalone
* transaction not within a dialog.
* <p>You can get a {@link SipSession} from {@link SipManager} with {@link
* SipManager#createSipSession createSipSession()} (when initiating calls) or {@link
* SipManager#getSessionFor getSessionFor()} (when receiving calls).</p>
*/
public final class SipSession {
private static final String TAG = "SipSession";
/**
* Defines SIP session states, such as "registering", "outgoing call", and "in call".
*/
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 for events relating to a SIP session, such as when a session is being registered
* ("on registering") or a call is outgoing ("on calling").
* <p>Many of these events are also received by {@link SipAudioCall.Listener}.</p>
*/
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 State}.
*
* @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 State#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 State#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 State#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 State#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 State#IN_CALL},
* {@link State#INCOMING_CALL},
* {@link State#OUTGOING_CALL} or
* {@link State#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 State#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

@@ -0,0 +1,64 @@
/*
* 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;
/**
* Adapter class for {@link ISipSessionListener}. Default implementation of all
* callback methods is no-op.
* @hide
*/
public class SipSessionAdapter extends ISipSessionListener.Stub {
public void onCalling(ISipSession session) {
}
public void onRinging(ISipSession session, SipProfile caller,
String sessionDescription) {
}
public void onRingingBack(ISipSession session) {
}
public void onCallEstablished(ISipSession session,
String sessionDescription) {
}
public void onCallEnded(ISipSession session) {
}
public void onCallBusy(ISipSession session) {
}
public void onCallChangeFailed(ISipSession session, int errorCode,
String message) {
}
public void onError(ISipSession session, int errorCode, String message) {
}
public void onRegistering(ISipSession session) {
}
public void onRegistrationDone(ISipSession session, int duration) {
}
public void onRegistrationFailed(ISipSession session, int errorCode,
String message) {
}
public void onRegistrationTimeout(ISipSession session) {
}
}

View File

@@ -0,0 +1,39 @@
<HTML>
<BODY>
<p>Provides access to Session Initiation Protocol (SIP) functionality, such as
making and answering VOIP calls using SIP.</p>
<p>To get started, you need to get an instance of the {@link android.net.sip.SipManager} by
calling {@link android.net.sip.SipManager#newInstance newInstance()}.</p>
<p>With the {@link android.net.sip.SipManager}, you can initiate SIP audio calls with {@link
android.net.sip.SipManager#makeAudioCall makeAudioCall()} and {@link
android.net.sip.SipManager#takeAudioCall takeAudioCall()}. Both methods require
a {@link android.net.sip.SipAudioCall.Listener} that receives callbacks when the state of the
call changes, such as when the call is ringing, established, or ended.</p>
<p>Both {@link android.net.sip.SipManager#makeAudioCall makeAudioCall()} also requires two
{@link android.net.sip.SipProfile} objects, representing the local device and the peer
device. You can create a {@link android.net.sip.SipProfile} using the {@link
android.net.sip.SipProfile.Builder} subclass.</p>
<p>Once you have a {@link android.net.sip.SipAudioCall}, you can perform SIP audio call actions with
the instance, such as make a call, answer a call, mute a call, turn on speaker mode, send DTMF
tones, and more.</p>
<p>If you want to create generic SIP connections (such as for video calls or other), you can
create a SIP connection from the {@link android.net.sip.SipManager}, using {@link
android.net.sip.SipManager#open open()}. If you only want to create audio SIP calls, though, you
should use the {@link android.net.sip.SipAudioCall} class, as described above.</p>
<p class="note"><strong>Note:</strong>
Not all Android-powered devices support VOIP functionality with SIP. Before performing any SIP
activity, you should call {@link android.net.sip.SipManager#isVoipSupported isVoipSupported()}
to verify that the device supports VOIP calling and {@link
android.net.sip.SipManager#isApiSupported isApiSupported()} to verify that the device supports the
SIP APIs.<br/><br/>
Your application must also request the {@link android.Manifest.permission#INTERNET} and {@link
android.Manifest.permission#USE_SIP} permissions in order to use the SIP APIs.
</p>
</BODY>
</HTML>