summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk8
-rw-r--r--CleanSpec.mk5
-rw-r--r--voip/java/android/net/rtp/AudioCodec.java146
-rw-r--r--voip/java/android/net/rtp/AudioGroup.java206
-rw-r--r--voip/java/android/net/rtp/AudioStream.java167
-rw-r--r--voip/java/android/net/rtp/RtpStream.java195
-rw-r--r--voip/java/android/net/rtp/package.html28
-rw-r--r--voip/java/android/net/sip/ISipService.aidl43
-rw-r--r--voip/java/android/net/sip/ISipSession.aidl148
-rw-r--r--voip/java/android/net/sip/ISipSessionListener.aidl133
-rw-r--r--voip/java/android/net/sip/SimpleSessionDescription.java613
-rw-r--r--voip/java/android/net/sip/SipAudioCall.java1143
-rw-r--r--voip/java/android/net/sip/SipErrorCode.java101
-rw-r--r--voip/java/android/net/sip/SipException.java37
-rw-r--r--voip/java/android/net/sip/SipManager.java622
-rw-r--r--voip/java/android/net/sip/SipProfile.aidl19
-rw-r--r--voip/java/android/net/sip/SipProfile.java502
-rw-r--r--voip/java/android/net/sip/SipRegistrationListener.java48
-rw-r--r--voip/java/android/net/sip/SipSession.java574
-rw-r--r--voip/java/android/net/sip/SipSessionAdapter.java68
-rw-r--r--voip/java/android/net/sip/package.html45
-rw-r--r--voip/java/com/android/server/sip/SipHelper.java537
-rw-r--r--voip/java/com/android/server/sip/SipService.java1262
-rw-r--r--voip/java/com/android/server/sip/SipSessionGroup.java1863
-rw-r--r--voip/java/com/android/server/sip/SipSessionListenerProxy.java265
-rw-r--r--voip/java/com/android/server/sip/SipWakeLock.java73
-rw-r--r--voip/java/com/android/server/sip/SipWakeupTimer.java336
-rw-r--r--voip/jni/rtp/AmrCodec.cpp272
-rw-r--r--voip/jni/rtp/Android.mk59
-rw-r--r--voip/jni/rtp/AudioCodec.cpp51
-rw-r--r--voip/jni/rtp/AudioCodec.h38
-rw-r--r--voip/jni/rtp/AudioGroup.cpp1073
-rw-r--r--voip/jni/rtp/EchoSuppressor.cpp196
-rw-r--r--voip/jni/rtp/EchoSuppressor.h58
-rw-r--r--voip/jni/rtp/G711Codec.cpp144
-rw-r--r--voip/jni/rtp/GsmCodec.cpp78
-rw-r--r--voip/jni/rtp/RtpStream.cpp113
-rw-r--r--voip/jni/rtp/rtp_jni.cpp32
-rw-r--r--voip/jni/rtp/util.cpp61
39 files changed, 9 insertions, 11353 deletions
diff --git a/Android.mk b/Android.mk
index f45f977..73fae02 100644
--- a/Android.mk
+++ b/Android.mk
@@ -219,10 +219,7 @@ LOCAL_SRC_FILES += \
telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \
telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
wifi/java/android/net/wifi/IWifiManager.aidl \
- wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
- voip/java/android/net/sip/ISipSession.aidl \
- voip/java/android/net/sip/ISipSessionListener.aidl \
- voip/java/android/net/sip/ISipService.aidl
+ wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
#
@@ -353,6 +350,8 @@ non_base_dirs := \
../../external/apache-http/src/org/apache/http \
../opt/telephony/src/java/android/telephony \
../opt/telephony/src/java/android/telephony/gsm \
+ ../opt/net/voip/src/java/android/net/rtp \
+ ../opt/net/voip/src/java/android/net/sip
# These are relative to frameworks/base
dirs_to_check_apis := \
@@ -405,6 +404,7 @@ framework_docs_LOCAL_JAVA_LIBRARIES := \
framework \
mms-common \
telephony-common \
+ voip-common \
framework_docs_LOCAL_MODULE_CLASS := JAVA_LIBRARIES
framework_docs_LOCAL_DROIDDOC_HTML_DIR := docs/html
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 06f7c54..2c7d16f 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -149,6 +149,11 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framew
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IDisplayMagnificationController.P)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IDisplayMagnificationMediator.java)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IDisplayMagnificationMediator.P)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/voip)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/librtp_jni_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/librtp_jni.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/librtp_jni.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/librtp_jni.so)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/voip/java/android/net/rtp/AudioCodec.java b/voip/java/android/net/rtp/AudioCodec.java
deleted file mode 100644
index 85255c8..0000000
--- a/voip/java/android/net/rtp/AudioCodec.java
+++ /dev/null
@@ -1,146 +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.rtp;
-
-import java.util.Arrays;
-
-/**
- * This class defines a collection of audio codecs to be used with
- * {@link AudioStream}s. Their parameters are designed to be exchanged using
- * Session Description Protocol (SDP). Most of the values listed here can be
- * found in RFC 3551, while others are described in separated standards.
- *
- * <p>Few simple configurations are defined as public static instances for the
- * convenience of direct uses. More complicated ones could be obtained using
- * {@link #getCodec(int, String, String)}. For example, one can use the
- * following snippet to create a mode-1-only AMR codec.</p>
- * <pre>
- * AudioCodec codec = AudioCodec.getCodec(100, "AMR/8000", "mode-set=1");
- * </pre>
- *
- * @see AudioStream
- */
-public class AudioCodec {
- /**
- * The RTP payload type of the encoding.
- */
- public final int type;
-
- /**
- * The encoding parameters to be used in the corresponding SDP attribute.
- */
- public final String rtpmap;
-
- /**
- * The format parameters to be used in the corresponding SDP attribute.
- */
- public final String fmtp;
-
- /**
- * G.711 u-law audio codec.
- */
- public static final AudioCodec PCMU = new AudioCodec(0, "PCMU/8000", null);
-
- /**
- * G.711 a-law audio codec.
- */
- public static final AudioCodec PCMA = new AudioCodec(8, "PCMA/8000", null);
-
- /**
- * GSM Full-Rate audio codec, also known as GSM-FR, GSM 06.10, GSM, or
- * simply FR.
- */
- public static final AudioCodec GSM = new AudioCodec(3, "GSM/8000", null);
-
- /**
- * GSM Enhanced Full-Rate audio codec, also known as GSM-EFR, GSM 06.60, or
- * simply EFR.
- */
- public static final AudioCodec GSM_EFR = new AudioCodec(96, "GSM-EFR/8000", null);
-
- /**
- * Adaptive Multi-Rate narrowband audio codec, also known as AMR or AMR-NB.
- * Currently CRC, robust sorting, and interleaving are not supported. See
- * more details about these features in RFC 4867.
- */
- public static final AudioCodec AMR = new AudioCodec(97, "AMR/8000", null);
-
- private static final AudioCodec[] sCodecs = {GSM_EFR, AMR, GSM, PCMU, PCMA};
-
- private AudioCodec(int type, String rtpmap, String fmtp) {
- this.type = type;
- this.rtpmap = rtpmap;
- this.fmtp = fmtp;
- }
-
- /**
- * Returns system supported audio codecs.
- */
- public static AudioCodec[] getCodecs() {
- return Arrays.copyOf(sCodecs, sCodecs.length);
- }
-
- /**
- * Creates an AudioCodec according to the given configuration.
- *
- * @param type The payload type of the encoding defined in RTP/AVP.
- * @param rtpmap The encoding parameters specified in the corresponding SDP
- * attribute, or null if it is not available.
- * @param fmtp The format parameters specified in the corresponding SDP
- * attribute, or null if it is not available.
- * @return The configured AudioCodec or {@code null} if it is not supported.
- */
- public static AudioCodec getCodec(int type, String rtpmap, String fmtp) {
- if (type < 0 || type > 127) {
- return null;
- }
-
- AudioCodec hint = null;
- if (rtpmap != null) {
- String clue = rtpmap.trim().toUpperCase();
- for (AudioCodec codec : sCodecs) {
- if (clue.startsWith(codec.rtpmap)) {
- String channels = clue.substring(codec.rtpmap.length());
- if (channels.length() == 0 || channels.equals("/1")) {
- hint = codec;
- }
- break;
- }
- }
- } else if (type < 96) {
- for (AudioCodec codec : sCodecs) {
- if (type == codec.type) {
- hint = codec;
- rtpmap = codec.rtpmap;
- break;
- }
- }
- }
-
- if (hint == null) {
- return null;
- }
- if (hint == AMR && fmtp != null) {
- String clue = fmtp.toLowerCase();
- if (clue.contains("crc=1") || clue.contains("robust-sorting=1") ||
- clue.contains("interleaving=")) {
- return null;
- }
- }
- return new AudioCodec(type, rtpmap, fmtp);
- }
-}
diff --git a/voip/java/android/net/rtp/AudioGroup.java b/voip/java/android/net/rtp/AudioGroup.java
deleted file mode 100644
index 8faeb88..0000000
--- a/voip/java/android/net/rtp/AudioGroup.java
+++ /dev/null
@@ -1,206 +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.rtp;
-
-import android.media.AudioManager;
-
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-
-/**
- * An AudioGroup is an audio hub for the speaker, the microphone, and
- * {@link AudioStream}s. Each of these components can be logically turned on
- * or off by calling {@link #setMode(int)} or {@link RtpStream#setMode(int)}.
- * The AudioGroup will go through these components and process them one by one
- * within its execution loop. The loop consists of four steps. First, for each
- * AudioStream not in {@link RtpStream#MODE_SEND_ONLY}, decodes its incoming
- * packets and stores in its buffer. Then, if the microphone is enabled,
- * processes the recorded audio and stores in its buffer. Third, if the speaker
- * is enabled, mixes all AudioStream buffers and plays back. Finally, for each
- * AudioStream not in {@link RtpStream#MODE_RECEIVE_ONLY}, mixes all other
- * buffers and sends back the encoded packets. An AudioGroup does nothing if
- * there is no AudioStream in it.
- *
- * <p>Few things must be noticed before using these classes. The performance is
- * highly related to the system load and the network bandwidth. Usually a
- * simpler {@link AudioCodec} costs fewer CPU cycles but requires more network
- * bandwidth, and vise versa. Using two AudioStreams at the same time doubles
- * not only the load but also the bandwidth. The condition varies from one
- * device to another, and developers should choose the right combination in
- * order to get the best result.</p>
- *
- * <p>It is sometimes useful to keep multiple AudioGroups at the same time. For
- * example, a Voice over IP (VoIP) application might want to put a conference
- * call on hold in order to make a new call but still allow people in the
- * conference call talking to each other. This can be done easily using two
- * AudioGroups, but there are some limitations. Since the speaker and the
- * microphone are globally shared resources, only one AudioGroup at a time is
- * allowed to run in a mode other than {@link #MODE_ON_HOLD}. The others will
- * be unable to acquire these resources and fail silently.</p>
- *
- * <p class="note">Using this class requires
- * {@link android.Manifest.permission#RECORD_AUDIO} permission. Developers
- * should set the audio mode to {@link AudioManager#MODE_IN_COMMUNICATION}
- * using {@link AudioManager#setMode(int)} and change it back when none of
- * the AudioGroups is in use.</p>
- *
- * @see AudioStream
- */
-public class AudioGroup {
- /**
- * This mode is similar to {@link #MODE_NORMAL} except the speaker and
- * the microphone are both disabled.
- */
- public static final int MODE_ON_HOLD = 0;
-
- /**
- * This mode is similar to {@link #MODE_NORMAL} except the microphone is
- * disabled.
- */
- public static final int MODE_MUTED = 1;
-
- /**
- * This mode indicates that the speaker, the microphone, and all
- * {@link AudioStream}s in the group are enabled. First, the packets
- * received from the streams are decoded and mixed with the audio recorded
- * from the microphone. Then, the results are played back to the speaker,
- * encoded and sent back to each stream.
- */
- public static final int MODE_NORMAL = 2;
-
- /**
- * This mode is similar to {@link #MODE_NORMAL} except the echo suppression
- * is enabled. It should be only used when the speaker phone is on.
- */
- public static final int MODE_ECHO_SUPPRESSION = 3;
-
- private static final int MODE_LAST = 3;
-
- private final Map<AudioStream, Integer> mStreams;
- private int mMode = MODE_ON_HOLD;
-
- private int mNative;
- static {
- System.loadLibrary("rtp_jni");
- }
-
- /**
- * Creates an empty AudioGroup.
- */
- public AudioGroup() {
- mStreams = new HashMap<AudioStream, Integer>();
- }
-
- /**
- * Returns the {@link AudioStream}s in this group.
- */
- public AudioStream[] getStreams() {
- synchronized (this) {
- return mStreams.keySet().toArray(new AudioStream[mStreams.size()]);
- }
- }
-
- /**
- * Returns the current mode.
- */
- public int getMode() {
- return mMode;
- }
-
- /**
- * Changes the current mode. It must be one of {@link #MODE_ON_HOLD},
- * {@link #MODE_MUTED}, {@link #MODE_NORMAL}, and
- * {@link #MODE_ECHO_SUPPRESSION}.
- *
- * @param mode The mode to change to.
- * @throws IllegalArgumentException if the mode is invalid.
- */
- public void setMode(int mode) {
- if (mode < 0 || mode > MODE_LAST) {
- throw new IllegalArgumentException("Invalid mode");
- }
- synchronized (this) {
- nativeSetMode(mode);
- mMode = mode;
- }
- }
-
- private native void nativeSetMode(int mode);
-
- // Package-private method used by AudioStream.join().
- synchronized void add(AudioStream stream) {
- if (!mStreams.containsKey(stream)) {
- try {
- AudioCodec codec = stream.getCodec();
- String codecSpec = String.format(Locale.US, "%d %s %s", codec.type,
- codec.rtpmap, codec.fmtp);
- int id = nativeAdd(stream.getMode(), stream.getSocket(),
- stream.getRemoteAddress().getHostAddress(),
- stream.getRemotePort(), codecSpec, stream.getDtmfType());
- mStreams.put(stream, id);
- } catch (NullPointerException e) {
- throw new IllegalStateException(e);
- }
- }
- }
-
- private native int nativeAdd(int mode, int socket, String remoteAddress,
- int remotePort, String codecSpec, int dtmfType);
-
- // Package-private method used by AudioStream.join().
- synchronized void remove(AudioStream stream) {
- Integer id = mStreams.remove(stream);
- if (id != null) {
- nativeRemove(id);
- }
- }
-
- private native void nativeRemove(int id);
-
- /**
- * Sends a DTMF digit to every {@link AudioStream} in this group. Currently
- * only event {@code 0} to {@code 15} are supported.
- *
- * @throws IllegalArgumentException if the event is invalid.
- */
- public void sendDtmf(int event) {
- if (event < 0 || event > 15) {
- throw new IllegalArgumentException("Invalid event");
- }
- synchronized (this) {
- nativeSendDtmf(event);
- }
- }
-
- private native void nativeSendDtmf(int event);
-
- /**
- * Removes every {@link AudioStream} in this group.
- */
- public void clear() {
- for (AudioStream stream : getStreams()) {
- stream.join(null);
- }
- }
-
- @Override
- protected void finalize() throws Throwable {
- nativeRemove(0);
- super.finalize();
- }
-}
diff --git a/voip/java/android/net/rtp/AudioStream.java b/voip/java/android/net/rtp/AudioStream.java
deleted file mode 100644
index 5cd1abc..0000000
--- a/voip/java/android/net/rtp/AudioStream.java
+++ /dev/null
@@ -1,167 +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.rtp;
-
-import java.net.InetAddress;
-import java.net.SocketException;
-
-/**
- * An AudioStream is a {@link RtpStream} which carrys audio payloads over
- * Real-time Transport Protocol (RTP). Two different classes are developed in
- * order to support various usages such as audio conferencing. An AudioStream
- * represents a remote endpoint which consists of a network mapping and a
- * configured {@link AudioCodec}. On the other side, An {@link AudioGroup}
- * represents a local endpoint which mixes all the AudioStreams and optionally
- * interacts with the speaker and the microphone at the same time. The simplest
- * usage includes one for each endpoints. For other combinations, developers
- * should be aware of the limitations described in {@link AudioGroup}.
- *
- * <p>An AudioStream becomes busy when it joins an AudioGroup. In this case most
- * of the setter methods are disabled. This is designed to ease the task of
- * managing native resources. One can always make an AudioStream leave its
- * AudioGroup by calling {@link #join(AudioGroup)} with {@code null} and put it
- * back after the modification is done.</p>
- *
- * <p class="note">Using this class requires
- * {@link android.Manifest.permission#INTERNET} permission.</p>
- *
- * @see RtpStream
- * @see AudioGroup
- */
-public class AudioStream extends RtpStream {
- private AudioCodec mCodec;
- private int mDtmfType = -1;
- private AudioGroup mGroup;
-
- /**
- * Creates an AudioStream on the given local address. Note that the local
- * port is assigned automatically to conform with RFC 3550.
- *
- * @param address The network address of the local host to bind to.
- * @throws SocketException if the address cannot be bound or a problem
- * occurs during binding.
- */
- public AudioStream(InetAddress address) throws SocketException {
- super(address);
- }
-
- /**
- * Returns {@code true} if the stream has already joined an
- * {@link AudioGroup}.
- */
- @Override
- public final boolean isBusy() {
- return mGroup != null;
- }
-
- /**
- * Returns the joined {@link AudioGroup}.
- */
- public AudioGroup getGroup() {
- return mGroup;
- }
-
- /**
- * Joins an {@link AudioGroup}. Each stream can join only one group at a
- * time. The group can be changed by passing a different one or removed
- * by calling this method with {@code null}.
- *
- * @param group The AudioGroup to join or {@code null} to leave.
- * @throws IllegalStateException if the stream is not properly configured.
- * @see AudioGroup
- */
- public void join(AudioGroup group) {
- synchronized (this) {
- if (mGroup == group) {
- return;
- }
- if (mGroup != null) {
- mGroup.remove(this);
- mGroup = null;
- }
- if (group != null) {
- group.add(this);
- mGroup = group;
- }
- }
- }
-
- /**
- * Returns the {@link AudioCodec}, or {@code null} if it is not set.
- *
- * @see #setCodec(AudioCodec)
- */
- public AudioCodec getCodec() {
- return mCodec;
- }
-
- /**
- * Sets the {@link AudioCodec}.
- *
- * @param codec The AudioCodec to be used.
- * @throws IllegalArgumentException if its type is used by DTMF.
- * @throws IllegalStateException if the stream is busy.
- */
- public void setCodec(AudioCodec codec) {
- if (isBusy()) {
- throw new IllegalStateException("Busy");
- }
- if (codec.type == mDtmfType) {
- throw new IllegalArgumentException("The type is used by DTMF");
- }
- mCodec = codec;
- }
-
- /**
- * Returns the RTP payload type for dual-tone multi-frequency (DTMF) digits,
- * or {@code -1} if it is not enabled.
- *
- * @see #setDtmfType(int)
- */
- public int getDtmfType() {
- return mDtmfType;
- }
-
- /**
- * Sets the RTP payload type for dual-tone multi-frequency (DTMF) digits.
- * The primary usage is to send digits to the remote gateway to perform
- * certain tasks, such as second-stage dialing. According to RFC 2833, the
- * RTP payload type for DTMF is assigned dynamically, so it must be in the
- * range of 96 and 127. One can use {@code -1} to disable DTMF and free up
- * the previous assigned type. This method cannot be called when the stream
- * already joined an {@link AudioGroup}.
- *
- * @param type The RTP payload type to be used or {@code -1} to disable it.
- * @throws IllegalArgumentException if the type is invalid or used by codec.
- * @throws IllegalStateException if the stream is busy.
- * @see AudioGroup#sendDtmf(int)
- */
- public void setDtmfType(int type) {
- if (isBusy()) {
- throw new IllegalStateException("Busy");
- }
- if (type != -1) {
- if (type < 96 || type > 127) {
- throw new IllegalArgumentException("Invalid type");
- }
- if (mCodec != null && type == mCodec.type) {
- throw new IllegalArgumentException("The type is used by codec");
- }
- }
- mDtmfType = type;
- }
-}
diff --git a/voip/java/android/net/rtp/RtpStream.java b/voip/java/android/net/rtp/RtpStream.java
deleted file mode 100644
index b9d75cd..0000000
--- a/voip/java/android/net/rtp/RtpStream.java
+++ /dev/null
@@ -1,195 +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.rtp;
-
-import java.net.InetAddress;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.SocketException;
-
-/**
- * RtpStream represents the base class of streams which send and receive network
- * packets with media payloads over Real-time Transport Protocol (RTP).
- *
- * <p class="note">Using this class requires
- * {@link android.Manifest.permission#INTERNET} permission.</p>
- */
-public class RtpStream {
- /**
- * This mode indicates that the stream sends and receives packets at the
- * same time. This is the initial mode for new streams.
- */
- public static final int MODE_NORMAL = 0;
-
- /**
- * This mode indicates that the stream only sends packets.
- */
- public static final int MODE_SEND_ONLY = 1;
-
- /**
- * This mode indicates that the stream only receives packets.
- */
- public static final int MODE_RECEIVE_ONLY = 2;
-
- private static final int MODE_LAST = 2;
-
- private final InetAddress mLocalAddress;
- private final int mLocalPort;
-
- private InetAddress mRemoteAddress;
- private int mRemotePort = -1;
- private int mMode = MODE_NORMAL;
-
- private int mSocket = -1;
- static {
- System.loadLibrary("rtp_jni");
- }
-
- /**
- * Creates a RtpStream on the given local address. Note that the local
- * port is assigned automatically to conform with RFC 3550.
- *
- * @param address The network address of the local host to bind to.
- * @throws SocketException if the address cannot be bound or a problem
- * occurs during binding.
- */
- RtpStream(InetAddress address) throws SocketException {
- mLocalPort = create(address.getHostAddress());
- mLocalAddress = address;
- }
-
- private native int create(String address) throws SocketException;
-
- /**
- * Returns the network address of the local host.
- */
- public InetAddress getLocalAddress() {
- return mLocalAddress;
- }
-
- /**
- * Returns the network port of the local host.
- */
- public int getLocalPort() {
- return mLocalPort;
- }
-
- /**
- * Returns the network address of the remote host or {@code null} if the
- * stream is not associated.
- */
- public InetAddress getRemoteAddress() {
- return mRemoteAddress;
- }
-
- /**
- * Returns the network port of the remote host or {@code -1} if the stream
- * is not associated.
- */
- public int getRemotePort() {
- return mRemotePort;
- }
-
- /**
- * Returns {@code true} if the stream is busy. In this case most of the
- * setter methods are disabled. This method is intended to be overridden
- * by subclasses.
- */
- public boolean isBusy() {
- return false;
- }
-
- /**
- * Returns the current mode.
- */
- public int getMode() {
- return mMode;
- }
-
- /**
- * Changes the current mode. It must be one of {@link #MODE_NORMAL},
- * {@link #MODE_SEND_ONLY}, and {@link #MODE_RECEIVE_ONLY}.
- *
- * @param mode The mode to change to.
- * @throws IllegalArgumentException if the mode is invalid.
- * @throws IllegalStateException if the stream is busy.
- * @see #isBusy()
- */
- public void setMode(int mode) {
- if (isBusy()) {
- throw new IllegalStateException("Busy");
- }
- if (mode < 0 || mode > MODE_LAST) {
- throw new IllegalArgumentException("Invalid mode");
- }
- mMode = mode;
- }
-
- /**
- * Associates with a remote host. This defines the destination of the
- * outgoing packets.
- *
- * @param address The network address of the remote host.
- * @param port The network port of the remote host.
- * @throws IllegalArgumentException if the address is not supported or the
- * port is invalid.
- * @throws IllegalStateException if the stream is busy.
- * @see #isBusy()
- */
- public void associate(InetAddress address, int port) {
- if (isBusy()) {
- throw new IllegalStateException("Busy");
- }
- if (!(address instanceof Inet4Address && mLocalAddress instanceof Inet4Address) &&
- !(address instanceof Inet6Address && mLocalAddress instanceof Inet6Address)) {
- throw new IllegalArgumentException("Unsupported address");
- }
- if (port < 0 || port > 65535) {
- throw new IllegalArgumentException("Invalid port");
- }
- mRemoteAddress = address;
- mRemotePort = port;
- }
-
- int getSocket() {
- return mSocket;
- }
-
- /**
- * Releases allocated resources. The stream becomes inoperable after calling
- * this method.
- *
- * @throws IllegalStateException if the stream is busy.
- * @see #isBusy()
- */
- public void release() {
- synchronized (this) {
- if (isBusy()) {
- throw new IllegalStateException("Busy");
- }
- close();
- }
- }
-
- private native void close();
-
- @Override
- protected void finalize() throws Throwable {
- close();
- super.finalize();
- }
-}
diff --git a/voip/java/android/net/rtp/package.html b/voip/java/android/net/rtp/package.html
deleted file mode 100644
index 4506b09..0000000
--- a/voip/java/android/net/rtp/package.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<html>
-<body>
-<p>Provides APIs for RTP (Real-time Transport Protocol), allowing applications to manage on-demand
-or interactive data streaming. In particular, apps that provide VOIP, push-to-talk, conferencing,
-and audio streaming can use these APIs to initiate sessions and transmit or receive data streams
-over any available network.</p>
-
-<p>To support audio conferencing and similar usages, you need to instantiate two classes as
-endpoints for the stream:</p>
-
-<ul>
-<li>{@link android.net.rtp.AudioStream} specifies a remote endpoint and consists of network mapping
-and a configured {@link android.net.rtp.AudioCodec}.</li>
-
-<li>{@link android.net.rtp.AudioGroup} represents the local endpoint for one or more {@link
-android.net.rtp.AudioStream}s. The {@link android.net.rtp.AudioGroup} mixes all the {@link
-android.net.rtp.AudioStream}s and optionally interacts with the device speaker and the microphone at
-the same time.</li>
-</ul>
-
-<p>The simplest usage involves a single remote endpoint and local endpoint. For more complex usages,
-refer to the limitations described for {@link android.net.rtp.AudioGroup}.</p>
-
-<p class="note"><strong>Note:</strong> To use the RTP APIs, you must request the {@link
-android.Manifest.permission#INTERNET} and {@link
-android.Manifest.permission#RECORD_AUDIO} permissions in your manifest file.</p>
-</body>
-</html> \ No newline at end of file
diff --git a/voip/java/android/net/sip/ISipService.aidl b/voip/java/android/net/sip/ISipService.aidl
deleted file mode 100644
index 3250bf9..0000000
--- a/voip/java/android/net/sip/ISipService.aidl
+++ /dev/null
@@ -1,43 +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.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();
-}
diff --git a/voip/java/android/net/sip/ISipSession.aidl b/voip/java/android/net/sip/ISipSession.aidl
deleted file mode 100644
index 2d515db..0000000
--- a/voip/java/android/net/sip/ISipSession.aidl
+++ /dev/null
@@ -1,148 +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.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);
-}
diff --git a/voip/java/android/net/sip/ISipSessionListener.aidl b/voip/java/android/net/sip/ISipSessionListener.aidl
deleted file mode 100644
index 690700c..0000000
--- a/voip/java/android/net/sip/ISipSessionListener.aidl
+++ /dev/null
@@ -1,133 +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.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 the call is being transferred to a new one.
- *
- * @param newSession the new session that the call will be transferred to
- * @param sessionDescription the new peer's session description
- */
- void onCallTransferring(in ISipSession newSession, String sessionDescription);
-
- /**
- * 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);
-}
diff --git a/voip/java/android/net/sip/SimpleSessionDescription.java b/voip/java/android/net/sip/SimpleSessionDescription.java
deleted file mode 100644
index 9fcd21d..0000000
--- a/voip/java/android/net/sip/SimpleSessionDescription.java
+++ /dev/null
@@ -1,613 +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 java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Locale;
-
-/**
- * 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(Locale.US, "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);
- }
- }
-}
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
deleted file mode 100644
index ea943e9..0000000
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ /dev/null
@@ -1,1143 +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.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.telephony.Rlog;
-import android.text.TextUtils;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
- * Handles an Internet audio call over SIP. You can instantiate this class with {@link SipManager},
- * using {@link SipManager#makeAudioCall makeAudioCall()} and {@link SipManager#takeAudioCall
- * takeAudioCall()}.
- *
- * <p class="note"><strong>Note:</strong> Using this class require the
- * {@link android.Manifest.permission#INTERNET} and
- * {@link android.Manifest.permission#USE_SIP} permissions. In addition, {@link
- * #startAudio} requires the
- * {@link android.Manifest.permission#RECORD_AUDIO},
- * {@link android.Manifest.permission#ACCESS_WIFI_STATE}, and
- * {@link android.Manifest.permission#WAKE_LOCK} permissions; and {@link #setSpeakerMode
- * setSpeakerMode()} requires the
- * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS} permission.</p>
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about using SIP, read the
- * <a href="{@docRoot}guide/topics/network/sip.html">Session Initiation Protocol</a>
- * developer guide.</p>
- * </div>
- */
-public class SipAudioCall {
- private static final String LOG_TAG = SipAudioCall.class.getSimpleName();
- private static final boolean DBG = true;
- 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 static final int TRANSFER_TIMEOUT = 15; // in seconds
-
- /** Listener for events relating to a SIP call, such as when a call is being
- * recieved ("on ringing") or a call is outgoing ("on calling").
- * <p>Many of these events are also received by {@link SipSession.Listener}.</p>
- */
- public static class Listener {
- /**
- * Called when the call object is ready to make another call.
- * The default implementation calls {@link #onChanged}.
- *
- * @param call the call object that is ready to make another call
- */
- public void onReadyToCall(SipAudioCall call) {
- onChanged(call);
- }
-
- /**
- * Called when a request is sent out to initiate a new call.
- * The default implementation calls {@link #onChanged}.
- *
- * @param call the call object that carries out the audio call
- */
- public void onCalling(SipAudioCall call) {
- onChanged(call);
- }
-
- /**
- * Called when a new call comes in.
- * The default implementation calls {@link #onChanged}.
- *
- * @param call the call object that carries out the audio call
- * @param caller the SIP profile of the caller
- */
- public void onRinging(SipAudioCall call, SipProfile caller) {
- onChanged(call);
- }
-
- /**
- * Called when a RINGING response is received for the INVITE request
- * sent. The default implementation calls {@link #onChanged}.
- *
- * @param call the call object that carries out the audio call
- */
- public void onRingingBack(SipAudioCall call) {
- onChanged(call);
- }
-
- /**
- * Called when the session is established.
- * The default implementation calls {@link #onChanged}.
- *
- * @param call the call object that carries out the audio call
- */
- public void onCallEstablished(SipAudioCall call) {
- onChanged(call);
- }
-
- /**
- * Called when the session is terminated.
- * The default implementation calls {@link #onChanged}.
- *
- * @param call the call object that carries out the audio call
- */
- public void onCallEnded(SipAudioCall call) {
- onChanged(call);
- }
-
- /**
- * Called when the peer is busy during session initialization.
- * The default implementation calls {@link #onChanged}.
- *
- * @param call the call object that carries out the audio call
- */
- public void onCallBusy(SipAudioCall call) {
- onChanged(call);
- }
-
- /**
- * Called when the call is on hold.
- * The default implementation calls {@link #onChanged}.
- *
- * @param call the call object that carries out the audio call
- */
- public void onCallHeld(SipAudioCall call) {
- onChanged(call);
- }
-
- /**
- * Called when an error occurs. The default implementation is no op.
- *
- * @param call the call object that carries out the audio call
- * @param errorCode error code of this error
- * @param errorMessage error message
- * @see SipErrorCode
- */
- public void onError(SipAudioCall call, int errorCode,
- String errorMessage) {
- // no-op
- }
-
- /**
- * Called when an event occurs and the corresponding callback is not
- * overridden. The default implementation is no op. Error events are
- * not re-directed to this callback and are handled in {@link #onError}.
- */
- public void onChanged(SipAudioCall call) {
- // no-op
- }
- }
-
- private Context mContext;
- private SipProfile mLocalProfile;
- private SipAudioCall.Listener mListener;
- private SipSession mSipSession;
- private SipSession mTransferringSession;
-
- 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 WifiManager mWm;
- private WifiManager.WifiLock mWifiHighPerfLock;
-
- private int mErrorCode = SipErrorCode.NO_ERROR;
- private String mErrorMessage;
-
- /**
- * Creates a call object with the local SIP profile.
- * @param context the context for accessing system services such as
- * ringtone, audio, WIFI etc
- */
- public SipAudioCall(Context context, SipProfile localProfile) {
- mContext = context;
- mLocalProfile = localProfile;
- mWm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- }
-
- /**
- * Sets the listener to listen to the audio call events. The method calls
- * {@link #setListener setListener(listener, false)}.
- *
- * @param listener to listen to the audio call events of this object
- * @see #setListener(Listener, boolean)
- */
- public void setListener(SipAudioCall.Listener listener) {
- setListener(listener, false);
- }
-
- /**
- * Sets the listener to listen to the audio call events. A
- * {@link SipAudioCall} can only hold one listener at a time. Subsequent
- * calls to this method override the previous listener.
- *
- * @param listener to listen to the audio call events of this object
- * @param callbackImmediately set to true if the caller wants to be called
- * back immediately on the current state
- */
- 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 SipSession.State.READY_TO_CALL:
- listener.onReadyToCall(this);
- break;
- case SipSession.State.INCOMING_CALL:
- listener.onRinging(this, getPeerProfile());
- break;
- case SipSession.State.OUTGOING_CALL:
- listener.onCalling(this);
- break;
- case SipSession.State.OUTGOING_CALL_RING_BACK:
- listener.onRingingBack(this);
- break;
- }
- }
- } catch (Throwable t) {
- loge("setListener()", t);
- }
- }
-
- /**
- * Checks if the call is established.
- *
- * @return true if the call is established
- */
- public boolean isInCall() {
- synchronized (this) {
- return mInCall;
- }
- }
-
- /**
- * Checks if the call is on hold.
- *
- * @return true if the call is on hold
- */
- public boolean isOnHold() {
- synchronized (this) {
- return mHold;
- }
- }
-
- /**
- * Closes this object. This object is not usable after being closed.
- */
- public void close() {
- close(true);
- }
-
- private synchronized void close(boolean closeRtp) {
- if (closeRtp) stopCall(RELEASE_SOCKET);
-
- mInCall = false;
- mHold = false;
- mSessionId = System.currentTimeMillis();
- mErrorCode = SipErrorCode.NO_ERROR;
- mErrorMessage = null;
-
- if (mSipSession != null) {
- mSipSession.setListener(null);
- mSipSession = null;
- }
- }
-
- /**
- * Gets the local SIP profile.
- *
- * @return the local SIP profile
- */
- public SipProfile getLocalProfile() {
- synchronized (this) {
- return mLocalProfile;
- }
- }
-
- /**
- * Gets the peer's SIP profile.
- *
- * @return the peer's SIP profile
- */
- public SipProfile getPeerProfile() {
- synchronized (this) {
- return (mSipSession == null) ? null : mSipSession.getPeerProfile();
- }
- }
-
- /**
- * Gets the state of the {@link SipSession} that carries this call.
- * The value returned must be one of the states in {@link SipSession.State}.
- *
- * @return the session state
- */
- public int getState() {
- synchronized (this) {
- if (mSipSession == null) return SipSession.State.READY_TO_CALL;
- return mSipSession.getState();
- }
- }
-
-
- /**
- * Gets the {@link SipSession} that carries this call.
- *
- * @return the session object that carries this call
- * @hide
- */
- public SipSession getSipSession() {
- synchronized (this) {
- return mSipSession;
- }
- }
-
- private synchronized void transferToNewSession() {
- if (mTransferringSession == null) return;
- SipSession origin = mSipSession;
- mSipSession = mTransferringSession;
- mTransferringSession = null;
-
- // stop the replaced call.
- if (mAudioStream != null) {
- mAudioStream.join(null);
- } else {
- try {
- mAudioStream = new AudioStream(InetAddress.getByName(
- getLocalIp()));
- } catch (Throwable t) {
- loge("transferToNewSession():", t);
- }
- }
- if (origin != null) origin.endCall();
- startAudio();
- }
-
- private SipSession.Listener createListener() {
- return new SipSession.Listener() {
- @Override
- public void onCalling(SipSession session) {
- if (DBG) log("onCalling: session=" + session);
- Listener listener = mListener;
- if (listener != null) {
- try {
- listener.onCalling(SipAudioCall.this);
- } catch (Throwable t) {
- loge("onCalling():", t);
- }
- }
- }
-
- @Override
- public void onRingingBack(SipSession session) {
- if (DBG) log("onRingingBackk: " + session);
- Listener listener = mListener;
- if (listener != null) {
- try {
- listener.onRingingBack(SipAudioCall.this);
- } catch (Throwable t) {
- loge("onRingingBack():", t);
- }
- }
- }
-
- @Override
- public void onRinging(SipSession session,
- SipProfile peerProfile, String sessionDescription) {
- // this callback is triggered only for reinvite.
- synchronized (SipAudioCall.this) {
- 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) {
- loge("onRinging():", e);
- session.endCall();
- }
- }
- }
-
- @Override
- public void onCallEstablished(SipSession session,
- String sessionDescription) {
- mPeerSd = sessionDescription;
- if (DBG) log("onCallEstablished(): " + mPeerSd);
-
- // TODO: how to notify the UI that the remote party is changed
- if ((mTransferringSession != null)
- && (session == mTransferringSession)) {
- transferToNewSession();
- return;
- }
-
- Listener listener = mListener;
- if (listener != null) {
- try {
- if (mHold) {
- listener.onCallHeld(SipAudioCall.this);
- } else {
- listener.onCallEstablished(SipAudioCall.this);
- }
- } catch (Throwable t) {
- loge("onCallEstablished(): ", t);
- }
- }
- }
-
- @Override
- public void onCallEnded(SipSession session) {
- if (DBG) log("onCallEnded: " + session + " mSipSession:" + mSipSession);
- // reset the trasnferring session if it is the one.
- if (session == mTransferringSession) {
- mTransferringSession = null;
- return;
- }
- // or ignore the event if the original session is being
- // transferred to the new one.
- if ((mTransferringSession != null) ||
- (session != mSipSession)) return;
-
- Listener listener = mListener;
- if (listener != null) {
- try {
- listener.onCallEnded(SipAudioCall.this);
- } catch (Throwable t) {
- loge("onCallEnded(): ", t);
- }
- }
- close();
- }
-
- @Override
- public void onCallBusy(SipSession session) {
- if (DBG) log("onCallBusy: " + session);
- Listener listener = mListener;
- if (listener != null) {
- try {
- listener.onCallBusy(SipAudioCall.this);
- } catch (Throwable t) {
- loge("onCallBusy(): ", t);
- }
- }
- close(false);
- }
-
- @Override
- public void onCallChangeFailed(SipSession session, int errorCode,
- String message) {
- if (DBG) log("onCallChangedFailed: " + message);
- mErrorCode = errorCode;
- mErrorMessage = message;
- Listener listener = mListener;
- if (listener != null) {
- try {
- listener.onError(SipAudioCall.this, mErrorCode,
- message);
- } catch (Throwable t) {
- loge("onCallBusy():", t);
- }
- }
- }
-
- @Override
- public void onError(SipSession session, int errorCode,
- String message) {
- SipAudioCall.this.onError(errorCode, message);
- }
-
- @Override
- public void onRegistering(SipSession session) {
- // irrelevant
- }
-
- @Override
- public void onRegistrationTimeout(SipSession session) {
- // irrelevant
- }
-
- @Override
- public void onRegistrationFailed(SipSession session, int errorCode,
- String message) {
- // irrelevant
- }
-
- @Override
- public void onRegistrationDone(SipSession session, int duration) {
- // irrelevant
- }
-
- @Override
- public void onCallTransferring(SipSession newSession,
- String sessionDescription) {
- if (DBG) log("onCallTransferring: mSipSession="
- + mSipSession + " newSession=" + newSession);
- mTransferringSession = newSession;
- try {
- if (sessionDescription == null) {
- newSession.makeCall(newSession.getPeerProfile(),
- createOffer().encode(), TRANSFER_TIMEOUT);
- } else {
- String answer = createAnswer(sessionDescription).encode();
- newSession.answerCall(answer, SESSION_TIMEOUT);
- }
- } catch (Throwable e) {
- loge("onCallTransferring()", e);
- newSession.endCall();
- }
- }
- };
- }
-
- private void onError(int errorCode, String message) {
- if (DBG) log("onError: "
- + SipErrorCode.toString(errorCode) + ": " + message);
- mErrorCode = errorCode;
- mErrorMessage = message;
- Listener listener = mListener;
- if (listener != null) {
- try {
- listener.onError(this, errorCode, message);
- } catch (Throwable t) {
- loge("onError():", t);
- }
- }
- synchronized (this) {
- if ((errorCode == SipErrorCode.DATA_CONNECTION_LOST)
- || !isInCall()) {
- close(true);
- }
- }
- }
-
- /**
- * Attaches an incoming call to this call object.
- *
- * @param session the session that receives the incoming call
- * @param sessionDescription the session description of the incoming call
- * @throws SipException if the SIP service fails to attach this object to
- * the session or VOIP API is not supported by the device
- * @see SipManager#isVoipSupported
- */
- public void attachCall(SipSession session, String sessionDescription)
- throws SipException {
- if (!SipManager.isVoipSupported(mContext)) {
- throw new SipException("VOIP API is not supported");
- }
-
- synchronized (this) {
- mSipSession = session;
- mPeerSd = sessionDescription;
- if (DBG) log("attachCall(): " + mPeerSd);
- try {
- session.setListener(createListener());
- } catch (Throwable e) {
- loge("attachCall()", e);
- throwSipException(e);
- }
- }
- }
-
- /**
- * Initiates an audio call to the specified profile. The attempt will be
- * timed out if the call is not established within {@code timeout} seconds
- * and {@link Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
- * will be called.
- *
- * @param peerProfile the SIP profile to make the call to
- * @param sipSession the {@link SipSession} for carrying out the call
- * @param timeout the timeout value in seconds. Default value (defined by
- * SIP protocol) is used if {@code timeout} is zero or negative.
- * @see Listener#onError
- * @throws SipException if the SIP service fails to create a session for the
- * call or VOIP API is not supported by the device
- * @see SipManager#isVoipSupported
- */
- public void makeCall(SipProfile peerProfile, SipSession sipSession,
- int timeout) throws SipException {
- if (DBG) log("makeCall: " + peerProfile + " session=" + sipSession + " timeout=" + timeout);
- if (!SipManager.isVoipSupported(mContext)) {
- throw new SipException("VOIP API is not supported");
- }
-
- synchronized (this) {
- mSipSession = sipSession;
- try {
- mAudioStream = new AudioStream(InetAddress.getByName(
- getLocalIp()));
- sipSession.setListener(createListener());
- sipSession.makeCall(peerProfile, createOffer().encode(),
- timeout);
- } catch (IOException e) {
- loge("makeCall:", e);
- throw new SipException("makeCall()", e);
- }
- }
- }
-
- /**
- * Ends a call.
- * @throws SipException if the SIP service fails to end the call
- */
- public void endCall() throws SipException {
- if (DBG) log("endCall: mSipSession" + mSipSession);
- synchronized (this) {
- stopCall(RELEASE_SOCKET);
- mInCall = false;
-
- // perform the above local ops first and then network op
- if (mSipSession != null) mSipSession.endCall();
- }
- }
-
- /**
- * Puts a call on hold. When succeeds, {@link Listener#onCallHeld} is
- * called. The attempt will be timed out if the call is not established
- * within {@code timeout} seconds and
- * {@link Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
- * will be called.
- *
- * @param timeout the timeout value in seconds. Default value (defined by
- * SIP protocol) is used if {@code timeout} is zero or negative.
- * @see Listener#onError
- * @throws SipException if the SIP service fails to hold the call
- */
- public void holdCall(int timeout) throws SipException {
- if (DBG) log("holdCall: mSipSession" + mSipSession + " timeout=" + timeout);
- synchronized (this) {
- if (mHold) return;
- if (mSipSession == null) {
- loge("holdCall:");
- throw new SipException("Not in a call to hold call");
- }
- mSipSession.changeCall(createHoldOffer().encode(), timeout);
- mHold = true;
- setAudioGroupMode();
- }
- }
-
- /**
- * Answers a call. The attempt will be timed out if the call is not
- * established within {@code timeout} seconds and
- * {@link Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
- * will be called.
- *
- * @param timeout the timeout value in seconds. Default value (defined by
- * SIP protocol) is used if {@code timeout} is zero or negative.
- * @see Listener#onError
- * @throws SipException if the SIP service fails to answer the call
- */
- public void answerCall(int timeout) throws SipException {
- if (DBG) log("answerCall: mSipSession" + mSipSession + " timeout=" + timeout);
- synchronized (this) {
- if (mSipSession == null) {
- throw new SipException("No call to answer");
- }
- try {
- mAudioStream = new AudioStream(InetAddress.getByName(
- getLocalIp()));
- mSipSession.answerCall(createAnswer(mPeerSd).encode(), timeout);
- } catch (IOException e) {
- loge("answerCall:", e);
- throw new SipException("answerCall()", e);
- }
- }
- }
-
- /**
- * Continues a call that's on hold. When succeeds,
- * {@link Listener#onCallEstablished} is called. The attempt will be timed
- * out if the call is not established within {@code timeout} seconds and
- * {@link Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
- * will be called.
- *
- * @param timeout the timeout value in seconds. Default value (defined by
- * SIP protocol) is used if {@code timeout} is zero or negative.
- * @see Listener#onError
- * @throws SipException if the SIP service fails to unhold the call
- */
- public void continueCall(int timeout) throws SipException {
- if (DBG) log("continueCall: mSipSession" + mSipSession + " timeout=" + timeout);
- synchronized (this) {
- if (!mHold) return;
- mSipSession.changeCall(createContinueOffer().encode(), timeout);
- mHold = false;
- setAudioGroupMode();
- }
- }
-
- 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");
- if (DBG) log("createOffer: offer=" + offer);
- return offer;
- }
-
- private SimpleSessionDescription createAnswer(String offerSd) {
- if (TextUtils.isEmpty(offerSd)) return createOffer();
- 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) {
- loge("createAnswer: no suitable codes");
- throw new IllegalStateException("Reject SDP: no suitable codecs");
- }
- if (DBG) log("createAnswer: answer=" + answer);
- return answer;
- }
-
- private SimpleSessionDescription createHoldOffer() {
- SimpleSessionDescription offer = createContinueOffer();
- offer.setAttribute("sendonly", "");
- if (DBG) log("createHoldOffer: offer=" + offer);
- return offer;
- }
-
- private SimpleSessionDescription createContinueOffer() {
- if (DBG) log("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) {
- if (DBG) log("grabWifiHighPerfLock:");
- mWifiHighPerfLock = ((WifiManager)
- mContext.getSystemService(Context.WIFI_SERVICE))
- .createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, LOG_TAG);
- mWifiHighPerfLock.acquire();
- }
- }
-
- private void releaseWifiHighPerfLock() {
- if (mWifiHighPerfLock != null) {
- if (DBG) log("releaseWifiHighPerfLock:");
- mWifiHighPerfLock.release();
- mWifiHighPerfLock = null;
- }
- }
-
- private boolean isWifiOn() {
- return (mWm.getConnectionInfo().getBSSID() == null) ? false : true;
- }
-
- /** Toggles mute. */
- public void toggleMute() {
- synchronized (this) {
- mMuted = !mMuted;
- setAudioGroupMode();
- }
- }
-
- /**
- * Checks if the call is muted.
- *
- * @return true if the call is muted
- */
- public boolean isMuted() {
- synchronized (this) {
- return mMuted;
- }
- }
-
- /**
- * Puts the device to speaker mode.
- * <p class="note"><strong>Note:</strong> Requires the
- * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS} permission.</p>
- *
- * @param speakerMode set true to enable speaker mode; false to disable
- */
- public void setSpeakerMode(boolean speakerMode) {
- synchronized (this) {
- ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
- .setSpeakerphoneOn(speakerMode);
- setAudioGroupMode();
- }
- }
-
- private boolean isSpeakerOn() {
- return ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
- .isSpeakerphoneOn();
- }
-
- /**
- * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2883</a>,
- * event 0--9 maps to decimal
- * value 0--9, '*' to 10, '#' to 11, event 'A'--'D' to 12--15, and event
- * flash to 16. Currently, event flash is not supported.
- *
- * @param code the DTMF code to send. Value 0 to 15 (inclusive) are valid
- * inputs.
- */
- public void sendDtmf(int code) {
- sendDtmf(code, null);
- }
-
- /**
- * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2883</a>,
- * event 0--9 maps to decimal
- * value 0--9, '*' to 10, '#' to 11, event 'A'--'D' to 12--15, and event
- * flash to 16. Currently, event flash is not supported.
- *
- * @param code the DTMF code to send. Value 0 to 15 (inclusive) are valid
- * inputs.
- * @param result the result message to send when done
- */
- public void sendDtmf(int code, Message result) {
- synchronized (this) {
- AudioGroup audioGroup = getAudioGroup();
- if ((audioGroup != null) && (mSipSession != null)
- && (SipSession.State.IN_CALL == getState())) {
- if (DBG) log("sendDtmf: code=" + code + " result=" + result);
- audioGroup.sendDtmf(code);
- }
- if (result != null) result.sendToTarget();
- }
- }
-
- /**
- * Gets the {@link AudioStream} object used in this call. The object
- * represents the RTP stream that carries the audio data to and from the
- * peer. The object may not be created before the call is established. And
- * it is undefined after the call ends or the {@link #close} method is
- * called.
- *
- * @return the {@link AudioStream} object or null if the RTP stream has not
- * yet been set up
- * @hide
- */
- public AudioStream getAudioStream() {
- synchronized (this) {
- return mAudioStream;
- }
- }
-
- /**
- * Gets the {@link AudioGroup} object which the {@link AudioStream} object
- * joins. The group object may not exist before the call is established.
- * Also, the {@code AudioStream} may change its group during a call (e.g.,
- * after the call is held/un-held). Finally, the {@code AudioGroup} object
- * returned by this method is undefined after the call ends or the
- * {@link #close} method is called. If a group object is set by
- * {@link #setAudioGroup(AudioGroup)}, then this method returns that object.
- *
- * @return the {@link AudioGroup} object or null if the RTP stream has not
- * yet been set up
- * @see #getAudioStream
- * @hide
- */
- public AudioGroup getAudioGroup() {
- synchronized (this) {
- if (mAudioGroup != null) return mAudioGroup;
- return ((mAudioStream == null) ? null : mAudioStream.getGroup());
- }
- }
-
- /**
- * Sets the {@link AudioGroup} object which the {@link AudioStream} object
- * joins. If {@code audioGroup} is null, then the {@code AudioGroup} object
- * will be dynamically created when needed. Note that the mode of the
- * {@code AudioGroup} is not changed according to the audio settings (i.e.,
- * hold, mute, speaker phone) of this object. This is mainly used to merge
- * multiple {@code SipAudioCall} objects to form a conference call. The
- * settings of the first object (that merges others) override others'.
- *
- * @see #getAudioStream
- * @hide
- */
- public void setAudioGroup(AudioGroup group) {
- synchronized (this) {
- if (DBG) log("setAudioGroup: group=" + group);
- if ((mAudioStream != null) && (mAudioStream.getGroup() != null)) {
- mAudioStream.join(group);
- }
- mAudioGroup = group;
- }
- }
-
- /**
- * Starts the audio for the established call. This method should be called
- * after {@link Listener#onCallEstablished} is called.
- * <p class="note"><strong>Note:</strong> Requires the
- * {@link android.Manifest.permission#RECORD_AUDIO},
- * {@link android.Manifest.permission#ACCESS_WIFI_STATE} and
- * {@link android.Manifest.permission#WAKE_LOCK} permissions.</p>
- */
- public void startAudio() {
- try {
- startAudioInternal();
- } catch (UnknownHostException e) {
- onError(SipErrorCode.PEER_NOT_REACHABLE, e.getMessage());
- } catch (Throwable e) {
- onError(SipErrorCode.CLIENT_ERROR, e.getMessage());
- }
- }
-
- private synchronized void startAudioInternal() throws UnknownHostException {
- if (DBG) loge("startAudioInternal: mPeerSd=" + mPeerSd);
- if (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();
-
- // AudioGroup logic:
- AudioGroup audioGroup = getAudioGroup();
- if (mHold) {
- // 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();
- stream.join(audioGroup);
- }
- setAudioGroupMode();
- }
-
- // set audio group mode based on current audio configuration
- private void setAudioGroupMode() {
- AudioGroup audioGroup = getAudioGroup();
- if (DBG) log("setAudioGroupMode: audioGroup=" + audioGroup);
- if (audioGroup != null) {
- if (mHold) {
- audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
- } else if (mMuted) {
- audioGroup.setMode(AudioGroup.MODE_MUTED);
- } else if (isSpeakerOn()) {
- audioGroup.setMode(AudioGroup.MODE_ECHO_SUPPRESSION);
- } else {
- audioGroup.setMode(AudioGroup.MODE_NORMAL);
- }
- }
- }
-
- private void stopCall(boolean releaseSocket) {
- if (DBG) log("stopCall: releaseSocket=" + releaseSocket);
- releaseWifiHighPerfLock();
- if (mAudioStream != null) {
- mAudioStream.join(null);
-
- if (releaseSocket) {
- mAudioStream.release();
- mAudioStream = null;
- }
- }
- }
-
- private String getLocalIp() {
- return mSipSession.getLocalIp();
- }
-
- private void throwSipException(Throwable throwable) throws SipException {
- if (throwable instanceof SipException) {
- throw (SipException) throwable;
- } else {
- throw new SipException("", throwable);
- }
- }
-
- private void log(String s) {
- Rlog.d(LOG_TAG, s);
- }
-
- private void loge(String s) {
- Rlog.e(LOG_TAG, s);
- }
-
- private void loge(String s, Throwable t) {
- Rlog.e(LOG_TAG, s, t);
- }
-}
diff --git a/voip/java/android/net/sip/SipErrorCode.java b/voip/java/android/net/sip/SipErrorCode.java
deleted file mode 100644
index 509728f..0000000
--- a/voip/java/android/net/sip/SipErrorCode.java
+++ /dev/null
@@ -1,101 +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 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() {
- }
-}
diff --git a/voip/java/android/net/sip/SipException.java b/voip/java/android/net/sip/SipException.java
deleted file mode 100644
index 0339395..0000000
--- a/voip/java/android/net/sip/SipException.java
+++ /dev/null
@@ -1,37 +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;
-
-/**
- * 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);
- }
-}
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
deleted file mode 100644
index a94232a..0000000
--- a/voip/java/android/net/sip/SipManager.java
+++ /dev/null
@@ -1,622 +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.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.telephony.Rlog;
-
-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. Your application must also request the {@link
- * android.Manifest.permission#INTERNET} and {@link android.Manifest.permission#USE_SIP}
- * permissions.</p>
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about using SIP, read the
- * <a href="{@docRoot}guide/topics/network/sip.html">Session Initiation Protocol</a>
- * developer guide.</p>
- * </div>
- */
-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()));
- if (session == null) {
- throw new SipException(
- "SipService.createSession() returns null");
- }
- 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()));
- if (session == null) {
- throw new SipException(
- "SipService.createSession() returns null");
- }
- 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 ((s == null) ? null : 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?
- Rlog.e(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");
- }
- }
-}
diff --git a/voip/java/android/net/sip/SipProfile.aidl b/voip/java/android/net/sip/SipProfile.aidl
deleted file mode 100644
index 3b6f68f..0000000
--- a/voip/java/android/net/sip/SipProfile.aidl
+++ /dev/null
@@ -1,19 +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;
-
-parcelable SipProfile;
diff --git a/voip/java/android/net/sip/SipProfile.java b/voip/java/android/net/sip/SipProfile.java
deleted file mode 100644
index 0ef754c..0000000
--- a/voip/java/android/net/sip/SipProfile.java
+++ /dev/null
@@ -1,502 +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.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>
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about using SIP, read the
- * <a href="{@docRoot}guide/topics/network/sip.html">Session Initiation Protocol</a>
- * developer guide.</p>
- * </div>
- */
-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 authentication username of the profile
- * @return this builder object
- */
- 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
- * is used in authentication instead.
- *
- * @return the authentication username
- * @see #getUserName
- */
- 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;
- }
-}
diff --git a/voip/java/android/net/sip/SipRegistrationListener.java b/voip/java/android/net/sip/SipRegistrationListener.java
deleted file mode 100644
index 9968cc7..0000000
--- a/voip/java/android/net/sip/SipRegistrationListener.java
+++ /dev/null
@@ -1,48 +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;
-
-/**
- * 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);
-}
diff --git a/voip/java/android/net/sip/SipSession.java b/voip/java/android/net/sip/SipSession.java
deleted file mode 100644
index edbc66f..0000000
--- a/voip/java/android/net/sip/SipSession.java
+++ /dev/null
@@ -1,574 +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.os.RemoteException;
-import android.telephony.Rlog;
-
-/**
- * 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;
-
- /** When ending a call. @hide */
- public static final int ENDING_CALL = 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 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 the call is being transferred to a new one.
- *
- * @hide
- * @param newSession the new session that the call will be transferred to
- * @param sessionDescription the new peer's session description
- */
- public void onCallTransferring(SipSession newSession,
- String sessionDescription) {
- }
-
- /**
- * 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) {
- loge("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) {
- loge("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) {
- loge("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) {
- loge("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) {
- loge("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) {
- loge("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) {
- loge("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) {
- loge("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) {
- loge("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) {
- loge("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) {
- loge("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) {
- loge("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) {
- loge("changeCall:", e);
- }
- }
-
- ISipSession getRealSession() {
- return mSession;
- }
-
- private ISipSessionListener createListener() {
- return new ISipSessionListener.Stub() {
- @Override
- public void onCalling(ISipSession session) {
- if (mListener != null) {
- mListener.onCalling(SipSession.this);
- }
- }
-
- @Override
- public void onRinging(ISipSession session, SipProfile caller,
- String sessionDescription) {
- if (mListener != null) {
- mListener.onRinging(SipSession.this, caller,
- sessionDescription);
- }
- }
-
- @Override
- public void onRingingBack(ISipSession session) {
- if (mListener != null) {
- mListener.onRingingBack(SipSession.this);
- }
- }
-
- @Override
- public void onCallEstablished(ISipSession session,
- String sessionDescription) {
- if (mListener != null) {
- mListener.onCallEstablished(SipSession.this,
- sessionDescription);
- }
- }
-
- @Override
- public void onCallEnded(ISipSession session) {
- if (mListener != null) {
- mListener.onCallEnded(SipSession.this);
- }
- }
-
- @Override
- public void onCallBusy(ISipSession session) {
- if (mListener != null) {
- mListener.onCallBusy(SipSession.this);
- }
- }
-
- @Override
- public void onCallTransferring(ISipSession session,
- String sessionDescription) {
- if (mListener != null) {
- mListener.onCallTransferring(
- new SipSession(session, SipSession.this.mListener),
- sessionDescription);
-
- }
- }
-
- @Override
- public void onCallChangeFailed(ISipSession session, int errorCode,
- String message) {
- if (mListener != null) {
- mListener.onCallChangeFailed(SipSession.this, errorCode,
- message);
- }
- }
-
- @Override
- public void onError(ISipSession session, int errorCode, String message) {
- if (mListener != null) {
- mListener.onError(SipSession.this, errorCode, message);
- }
- }
-
- @Override
- public void onRegistering(ISipSession session) {
- if (mListener != null) {
- mListener.onRegistering(SipSession.this);
- }
- }
-
- @Override
- public void onRegistrationDone(ISipSession session, int duration) {
- if (mListener != null) {
- mListener.onRegistrationDone(SipSession.this, duration);
- }
- }
-
- @Override
- public void onRegistrationFailed(ISipSession session, int errorCode,
- String message) {
- if (mListener != null) {
- mListener.onRegistrationFailed(SipSession.this, errorCode,
- message);
- }
- }
-
- @Override
- public void onRegistrationTimeout(ISipSession session) {
- if (mListener != null) {
- mListener.onRegistrationTimeout(SipSession.this);
- }
- }
- };
- }
-
- private void loge(String s, Throwable t) {
- Rlog.e(TAG, s, t);
- }
-}
diff --git a/voip/java/android/net/sip/SipSessionAdapter.java b/voip/java/android/net/sip/SipSessionAdapter.java
deleted file mode 100644
index f538983..0000000
--- a/voip/java/android/net/sip/SipSessionAdapter.java
+++ /dev/null
@@ -1,68 +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;
-
-/**
- * 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 onCallTransferring(ISipSession session,
- String sessionDescription) {
- }
-
- 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) {
- }
-}
diff --git a/voip/java/android/net/sip/package.html b/voip/java/android/net/sip/package.html
deleted file mode 100644
index 3c4cc23..0000000
--- a/voip/java/android/net/sip/package.html
+++ /dev/null
@@ -1,45 +0,0 @@
-<HTML>
-<BODY>
-<p>Provides access to Session Initiation Protocol (SIP) functionality, such as
-making and answering VOIP calls using SIP.</p>
-
-<p>For more information, see the
-<a href="{@docRoot}guide/topics/connectivity/sip.html">Session Initiation Protocol</a>
-developer guide.</p>
-{@more}
-
-<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.
-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> \ No newline at end of file
diff --git a/voip/java/com/android/server/sip/SipHelper.java b/voip/java/com/android/server/sip/SipHelper.java
deleted file mode 100644
index c708be8..0000000
--- a/voip/java/com/android/server/sip/SipHelper.java
+++ /dev/null
@@ -1,537 +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 com.android.server.sip;
-
-import gov.nist.javax.sip.SipStackExt;
-import gov.nist.javax.sip.clientauthutils.AccountManager;
-import gov.nist.javax.sip.clientauthutils.AuthenticationHelper;
-import gov.nist.javax.sip.header.extensions.ReferencesHeader;
-import gov.nist.javax.sip.header.extensions.ReferredByHeader;
-import gov.nist.javax.sip.header.extensions.ReplacesHeader;
-
-import android.net.sip.SipProfile;
-import android.telephony.Rlog;
-
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.EventObject;
-import java.util.List;
-import java.util.regex.Pattern;
-
-import javax.sip.ClientTransaction;
-import javax.sip.Dialog;
-import javax.sip.DialogTerminatedEvent;
-import javax.sip.InvalidArgumentException;
-import javax.sip.ListeningPoint;
-import javax.sip.PeerUnavailableException;
-import javax.sip.RequestEvent;
-import javax.sip.ResponseEvent;
-import javax.sip.ServerTransaction;
-import javax.sip.SipException;
-import javax.sip.SipFactory;
-import javax.sip.SipProvider;
-import javax.sip.SipStack;
-import javax.sip.Transaction;
-import javax.sip.TransactionTerminatedEvent;
-import javax.sip.TransactionState;
-import javax.sip.address.Address;
-import javax.sip.address.AddressFactory;
-import javax.sip.address.SipURI;
-import javax.sip.header.CSeqHeader;
-import javax.sip.header.CallIdHeader;
-import javax.sip.header.ContactHeader;
-import javax.sip.header.FromHeader;
-import javax.sip.header.Header;
-import javax.sip.header.HeaderFactory;
-import javax.sip.header.MaxForwardsHeader;
-import javax.sip.header.ToHeader;
-import javax.sip.header.ViaHeader;
-import javax.sip.message.Message;
-import javax.sip.message.MessageFactory;
-import javax.sip.message.Request;
-import javax.sip.message.Response;
-
-/**
- * Helper class for holding SIP stack related classes and for various low-level
- * SIP tasks like sending messages.
- */
-class SipHelper {
- private static final String TAG = SipHelper.class.getSimpleName();
- private static final boolean DBG = false;
- private static final boolean DBG_PING = false;
-
- private SipStack mSipStack;
- private SipProvider mSipProvider;
- private AddressFactory mAddressFactory;
- private HeaderFactory mHeaderFactory;
- private MessageFactory mMessageFactory;
-
- public SipHelper(SipStack sipStack, SipProvider sipProvider)
- throws PeerUnavailableException {
- mSipStack = sipStack;
- mSipProvider = sipProvider;
-
- SipFactory sipFactory = SipFactory.getInstance();
- mAddressFactory = sipFactory.createAddressFactory();
- mHeaderFactory = sipFactory.createHeaderFactory();
- mMessageFactory = sipFactory.createMessageFactory();
- }
-
- private FromHeader createFromHeader(SipProfile profile, String tag)
- throws ParseException {
- return mHeaderFactory.createFromHeader(profile.getSipAddress(), tag);
- }
-
- private ToHeader createToHeader(SipProfile profile) throws ParseException {
- return createToHeader(profile, null);
- }
-
- private ToHeader createToHeader(SipProfile profile, String tag)
- throws ParseException {
- return mHeaderFactory.createToHeader(profile.getSipAddress(), tag);
- }
-
- private CallIdHeader createCallIdHeader() {
- return mSipProvider.getNewCallId();
- }
-
- private CSeqHeader createCSeqHeader(String method)
- throws ParseException, InvalidArgumentException {
- long sequence = (long) (Math.random() * 10000);
- return mHeaderFactory.createCSeqHeader(sequence, method);
- }
-
- private MaxForwardsHeader createMaxForwardsHeader()
- throws InvalidArgumentException {
- return mHeaderFactory.createMaxForwardsHeader(70);
- }
-
- private MaxForwardsHeader createMaxForwardsHeader(int max)
- throws InvalidArgumentException {
- return mHeaderFactory.createMaxForwardsHeader(max);
- }
-
- private ListeningPoint getListeningPoint() throws SipException {
- ListeningPoint lp = mSipProvider.getListeningPoint(ListeningPoint.UDP);
- if (lp == null) lp = mSipProvider.getListeningPoint(ListeningPoint.TCP);
- if (lp == null) {
- ListeningPoint[] lps = mSipProvider.getListeningPoints();
- if ((lps != null) && (lps.length > 0)) lp = lps[0];
- }
- if (lp == null) {
- throw new SipException("no listening point is available");
- }
- return lp;
- }
-
- private List<ViaHeader> createViaHeaders()
- throws ParseException, SipException {
- List<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(1);
- ListeningPoint lp = getListeningPoint();
- ViaHeader viaHeader = mHeaderFactory.createViaHeader(lp.getIPAddress(),
- lp.getPort(), lp.getTransport(), null);
- viaHeader.setRPort();
- viaHeaders.add(viaHeader);
- return viaHeaders;
- }
-
- private ContactHeader createContactHeader(SipProfile profile)
- throws ParseException, SipException {
- return createContactHeader(profile, null, 0);
- }
-
- private ContactHeader createContactHeader(SipProfile profile,
- String ip, int port) throws ParseException,
- SipException {
- SipURI contactURI = (ip == null)
- ? createSipUri(profile.getUserName(), profile.getProtocol(),
- getListeningPoint())
- : createSipUri(profile.getUserName(), profile.getProtocol(),
- ip, port);
-
- Address contactAddress = mAddressFactory.createAddress(contactURI);
- contactAddress.setDisplayName(profile.getDisplayName());
-
- return mHeaderFactory.createContactHeader(contactAddress);
- }
-
- private ContactHeader createWildcardContactHeader() {
- ContactHeader contactHeader = mHeaderFactory.createContactHeader();
- contactHeader.setWildCard();
- return contactHeader;
- }
-
- private SipURI createSipUri(String username, String transport,
- ListeningPoint lp) throws ParseException {
- return createSipUri(username, transport, lp.getIPAddress(), lp.getPort());
- }
-
- private SipURI createSipUri(String username, String transport,
- String ip, int port) throws ParseException {
- SipURI uri = mAddressFactory.createSipURI(username, ip);
- try {
- uri.setPort(port);
- uri.setTransportParam(transport);
- } catch (InvalidArgumentException e) {
- throw new RuntimeException(e);
- }
- return uri;
- }
-
- public ClientTransaction sendOptions(SipProfile caller, SipProfile callee,
- String tag) throws SipException {
- try {
- Request request = (caller == callee)
- ? createRequest(Request.OPTIONS, caller, tag)
- : createRequest(Request.OPTIONS, caller, callee, tag);
-
- ClientTransaction clientTransaction =
- mSipProvider.getNewClientTransaction(request);
- clientTransaction.sendRequest();
- return clientTransaction;
- } catch (Exception e) {
- throw new SipException("sendOptions()", e);
- }
- }
-
- public ClientTransaction sendRegister(SipProfile userProfile, String tag,
- int expiry) throws SipException {
- try {
- Request request = createRequest(Request.REGISTER, userProfile, tag);
- if (expiry == 0) {
- // remove all previous registrations by wildcard
- // rfc3261#section-10.2.2
- request.addHeader(createWildcardContactHeader());
- } else {
- request.addHeader(createContactHeader(userProfile));
- }
- request.addHeader(mHeaderFactory.createExpiresHeader(expiry));
-
- ClientTransaction clientTransaction =
- mSipProvider.getNewClientTransaction(request);
- clientTransaction.sendRequest();
- return clientTransaction;
- } catch (ParseException e) {
- throw new SipException("sendRegister()", e);
- }
- }
-
- private Request createRequest(String requestType, SipProfile userProfile,
- String tag) throws ParseException, SipException {
- FromHeader fromHeader = createFromHeader(userProfile, tag);
- ToHeader toHeader = createToHeader(userProfile);
-
- String replaceStr = Pattern.quote(userProfile.getUserName() + "@");
- SipURI requestURI = mAddressFactory.createSipURI(
- userProfile.getUriString().replaceFirst(replaceStr, ""));
-
- List<ViaHeader> viaHeaders = createViaHeaders();
- CallIdHeader callIdHeader = createCallIdHeader();
- CSeqHeader cSeqHeader = createCSeqHeader(requestType);
- MaxForwardsHeader maxForwards = createMaxForwardsHeader();
- Request request = mMessageFactory.createRequest(requestURI,
- requestType, callIdHeader, cSeqHeader, fromHeader,
- toHeader, viaHeaders, maxForwards);
- Header userAgentHeader = mHeaderFactory.createHeader("User-Agent",
- "SIPAUA/0.1.001");
- request.addHeader(userAgentHeader);
- return request;
- }
-
- public ClientTransaction handleChallenge(ResponseEvent responseEvent,
- AccountManager accountManager) throws SipException {
- AuthenticationHelper authenticationHelper =
- ((SipStackExt) mSipStack).getAuthenticationHelper(
- accountManager, mHeaderFactory);
- ClientTransaction tid = responseEvent.getClientTransaction();
- ClientTransaction ct = authenticationHelper.handleChallenge(
- responseEvent.getResponse(), tid, mSipProvider, 5);
- if (DBG) log("send request with challenge response: "
- + ct.getRequest());
- ct.sendRequest();
- return ct;
- }
-
- private Request createRequest(String requestType, SipProfile caller,
- SipProfile callee, String tag) throws ParseException, SipException {
- FromHeader fromHeader = createFromHeader(caller, tag);
- ToHeader toHeader = createToHeader(callee);
- SipURI requestURI = callee.getUri();
- List<ViaHeader> viaHeaders = createViaHeaders();
- CallIdHeader callIdHeader = createCallIdHeader();
- CSeqHeader cSeqHeader = createCSeqHeader(requestType);
- MaxForwardsHeader maxForwards = createMaxForwardsHeader();
-
- Request request = mMessageFactory.createRequest(requestURI,
- requestType, callIdHeader, cSeqHeader, fromHeader,
- toHeader, viaHeaders, maxForwards);
-
- request.addHeader(createContactHeader(caller));
- return request;
- }
-
- public ClientTransaction sendInvite(SipProfile caller, SipProfile callee,
- String sessionDescription, String tag, ReferredByHeader referredBy,
- String replaces) throws SipException {
- try {
- Request request = createRequest(Request.INVITE, caller, callee, tag);
- if (referredBy != null) request.addHeader(referredBy);
- if (replaces != null) {
- request.addHeader(mHeaderFactory.createHeader(
- ReplacesHeader.NAME, replaces));
- }
- request.setContent(sessionDescription,
- mHeaderFactory.createContentTypeHeader(
- "application", "sdp"));
- ClientTransaction clientTransaction =
- mSipProvider.getNewClientTransaction(request);
- if (DBG) log("send INVITE: " + request);
- clientTransaction.sendRequest();
- return clientTransaction;
- } catch (ParseException e) {
- throw new SipException("sendInvite()", e);
- }
- }
-
- public ClientTransaction sendReinvite(Dialog dialog,
- String sessionDescription) throws SipException {
- try {
- Request request = dialog.createRequest(Request.INVITE);
- request.setContent(sessionDescription,
- mHeaderFactory.createContentTypeHeader(
- "application", "sdp"));
-
- // Adding rport argument in the request could fix some SIP servers
- // in resolving the initiator's NAT port mapping for relaying the
- // response message from the other end.
-
- ViaHeader viaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
- if (viaHeader != null) viaHeader.setRPort();
-
- ClientTransaction clientTransaction =
- mSipProvider.getNewClientTransaction(request);
- if (DBG) log("send RE-INVITE: " + request);
- dialog.sendRequest(clientTransaction);
- return clientTransaction;
- } catch (ParseException e) {
- throw new SipException("sendReinvite()", e);
- }
- }
-
- public ServerTransaction getServerTransaction(RequestEvent event)
- throws SipException {
- ServerTransaction transaction = event.getServerTransaction();
- if (transaction == null) {
- Request request = event.getRequest();
- return mSipProvider.getNewServerTransaction(request);
- } else {
- return transaction;
- }
- }
-
- /**
- * @param event the INVITE request event
- */
- public ServerTransaction sendRinging(RequestEvent event, String tag)
- throws SipException {
- try {
- Request request = event.getRequest();
- ServerTransaction transaction = getServerTransaction(event);
-
- Response response = mMessageFactory.createResponse(Response.RINGING,
- request);
-
- ToHeader toHeader = (ToHeader) response.getHeader(ToHeader.NAME);
- toHeader.setTag(tag);
- response.addHeader(toHeader);
- if (DBG) log("send RINGING: " + response);
- transaction.sendResponse(response);
- return transaction;
- } catch (ParseException e) {
- throw new SipException("sendRinging()", e);
- }
- }
-
- /**
- * @param event the INVITE request event
- */
- public ServerTransaction sendInviteOk(RequestEvent event,
- SipProfile localProfile, String sessionDescription,
- ServerTransaction inviteTransaction, String externalIp,
- int externalPort) throws SipException {
- try {
- Request request = event.getRequest();
- Response response = mMessageFactory.createResponse(Response.OK,
- request);
- response.addHeader(createContactHeader(localProfile, externalIp,
- externalPort));
- response.setContent(sessionDescription,
- mHeaderFactory.createContentTypeHeader(
- "application", "sdp"));
-
- if (inviteTransaction == null) {
- inviteTransaction = getServerTransaction(event);
- }
-
- if (inviteTransaction.getState() != TransactionState.COMPLETED) {
- if (DBG) log("send OK: " + response);
- inviteTransaction.sendResponse(response);
- }
-
- return inviteTransaction;
- } catch (ParseException e) {
- throw new SipException("sendInviteOk()", e);
- }
- }
-
- public void sendInviteBusyHere(RequestEvent event,
- ServerTransaction inviteTransaction) throws SipException {
- try {
- Request request = event.getRequest();
- Response response = mMessageFactory.createResponse(
- Response.BUSY_HERE, request);
-
- if (inviteTransaction == null) {
- inviteTransaction = getServerTransaction(event);
- }
-
- if (inviteTransaction.getState() != TransactionState.COMPLETED) {
- if (DBG) log("send BUSY HERE: " + response);
- inviteTransaction.sendResponse(response);
- }
- } catch (ParseException e) {
- throw new SipException("sendInviteBusyHere()", e);
- }
- }
-
- /**
- * @param event the INVITE ACK request event
- */
- public void sendInviteAck(ResponseEvent event, Dialog dialog)
- throws SipException {
- Response response = event.getResponse();
- long cseq = ((CSeqHeader) response.getHeader(CSeqHeader.NAME))
- .getSeqNumber();
- Request ack = dialog.createAck(cseq);
- if (DBG) log("send ACK: " + ack);
- dialog.sendAck(ack);
- }
-
- public void sendBye(Dialog dialog) throws SipException {
- Request byeRequest = dialog.createRequest(Request.BYE);
- if (DBG) log("send BYE: " + byeRequest);
- dialog.sendRequest(mSipProvider.getNewClientTransaction(byeRequest));
- }
-
- public void sendCancel(ClientTransaction inviteTransaction)
- throws SipException {
- Request cancelRequest = inviteTransaction.createCancel();
- if (DBG) log("send CANCEL: " + cancelRequest);
- mSipProvider.getNewClientTransaction(cancelRequest).sendRequest();
- }
-
- public void sendResponse(RequestEvent event, int responseCode)
- throws SipException {
- try {
- Request request = event.getRequest();
- Response response = mMessageFactory.createResponse(
- responseCode, request);
- if (DBG && (!Request.OPTIONS.equals(request.getMethod())
- || DBG_PING)) {
- log("send response: " + response);
- }
- getServerTransaction(event).sendResponse(response);
- } catch (ParseException e) {
- throw new SipException("sendResponse()", e);
- }
- }
-
- public void sendReferNotify(Dialog dialog, String content)
- throws SipException {
- try {
- Request request = dialog.createRequest(Request.NOTIFY);
- request.addHeader(mHeaderFactory.createSubscriptionStateHeader(
- "active;expires=60"));
- // set content here
- request.setContent(content,
- mHeaderFactory.createContentTypeHeader(
- "message", "sipfrag"));
- request.addHeader(mHeaderFactory.createEventHeader(
- ReferencesHeader.REFER));
- if (DBG) log("send NOTIFY: " + request);
- dialog.sendRequest(mSipProvider.getNewClientTransaction(request));
- } catch (ParseException e) {
- throw new SipException("sendReferNotify()", e);
- }
- }
-
- public void sendInviteRequestTerminated(Request inviteRequest,
- ServerTransaction inviteTransaction) throws SipException {
- try {
- Response response = mMessageFactory.createResponse(
- Response.REQUEST_TERMINATED, inviteRequest);
- if (DBG) log("send response: " + response);
- inviteTransaction.sendResponse(response);
- } catch (ParseException e) {
- throw new SipException("sendInviteRequestTerminated()", e);
- }
- }
-
- public static String getCallId(EventObject event) {
- if (event == null) return null;
- if (event instanceof RequestEvent) {
- return getCallId(((RequestEvent) event).getRequest());
- } else if (event instanceof ResponseEvent) {
- return getCallId(((ResponseEvent) event).getResponse());
- } else if (event instanceof DialogTerminatedEvent) {
- Dialog dialog = ((DialogTerminatedEvent) event).getDialog();
- return getCallId(((DialogTerminatedEvent) event).getDialog());
- } else if (event instanceof TransactionTerminatedEvent) {
- TransactionTerminatedEvent e = (TransactionTerminatedEvent) event;
- return getCallId(e.isServerTransaction()
- ? e.getServerTransaction()
- : e.getClientTransaction());
- } else {
- Object source = event.getSource();
- if (source instanceof Transaction) {
- return getCallId(((Transaction) source));
- } else if (source instanceof Dialog) {
- return getCallId((Dialog) source);
- }
- }
- return "";
- }
-
- public static String getCallId(Transaction transaction) {
- return ((transaction != null) ? getCallId(transaction.getRequest())
- : "");
- }
-
- private static String getCallId(Message message) {
- CallIdHeader callIdHeader =
- (CallIdHeader) message.getHeader(CallIdHeader.NAME);
- return callIdHeader.getCallId();
- }
-
- private static String getCallId(Dialog dialog) {
- return dialog.getCallId().getCallId();
- }
-
- private void log(String s) {
- Rlog.d(TAG, s);
- }
-}
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
deleted file mode 100644
index 80fe68c..0000000
--- a/voip/java/com/android/server/sip/SipService.java
+++ /dev/null
@@ -1,1262 +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 com.android.server.sip;
-
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.sip.ISipService;
-import android.net.sip.ISipSession;
-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.wifi.WifiManager;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.telephony.Rlog;
-
-import java.io.IOException;
-import java.net.DatagramSocket;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.Executor;
-import javax.sip.SipException;
-
-/**
- * @hide
- */
-public final class SipService extends ISipService.Stub {
- static final String TAG = "SipService";
- static final boolean DBG = true;
- private static final int EXPIRY_TIME = 3600;
- private static final int SHORT_EXPIRY_TIME = 10;
- private static final int MIN_EXPIRY_TIME = 60;
- private static final int DEFAULT_KEEPALIVE_INTERVAL = 10; // in seconds
- private static final int DEFAULT_MAX_KEEPALIVE_INTERVAL = 120; // in seconds
-
- private Context mContext;
- private String mLocalIp;
- private int mNetworkType = -1;
- private SipWakeupTimer mTimer;
- private WifiManager.WifiLock mWifiLock;
- private boolean mSipOnWifiOnly;
-
- private SipKeepAliveProcessCallback mSipKeepAliveProcessCallback;
-
- private MyExecutor mExecutor = new MyExecutor();
-
- // SipProfile URI --> group
- private Map<String, SipSessionGroupExt> mSipGroups =
- new HashMap<String, SipSessionGroupExt>();
-
- // session ID --> session
- private Map<String, ISipSession> mPendingSessions =
- new HashMap<String, ISipSession>();
-
- private ConnectivityReceiver mConnectivityReceiver;
- private SipWakeLock mMyWakeLock;
- private int mKeepAliveInterval;
- private int mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
-
- /**
- * Starts the SIP service. Do nothing if the SIP API is not supported on the
- * device.
- */
- public static void start(Context context) {
- if (SipManager.isApiSupported(context)) {
- ServiceManager.addService("sip", new SipService(context));
- context.sendBroadcast(new Intent(SipManager.ACTION_SIP_SERVICE_UP));
- if (DBG) slog("start:");
- }
- }
-
- private SipService(Context context) {
- if (DBG) log("SipService: started!");
- mContext = context;
- mConnectivityReceiver = new ConnectivityReceiver();
-
- mWifiLock = ((WifiManager)
- context.getSystemService(Context.WIFI_SERVICE))
- .createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
- mWifiLock.setReferenceCounted(false);
- mSipOnWifiOnly = SipManager.isSipWifiOnly(context);
-
- mMyWakeLock = new SipWakeLock((PowerManager)
- context.getSystemService(Context.POWER_SERVICE));
-
- mTimer = new SipWakeupTimer(context, mExecutor);
- }
-
- @Override
- public synchronized SipProfile[] getListOfProfiles() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- boolean isCallerRadio = isCallerRadio();
- ArrayList<SipProfile> profiles = new ArrayList<SipProfile>();
- for (SipSessionGroupExt group : mSipGroups.values()) {
- if (isCallerRadio || isCallerCreator(group)) {
- profiles.add(group.getLocalProfile());
- }
- }
- return profiles.toArray(new SipProfile[profiles.size()]);
- }
-
- @Override
- public synchronized void open(SipProfile localProfile) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- localProfile.setCallingUid(Binder.getCallingUid());
- try {
- createGroup(localProfile);
- } catch (SipException e) {
- loge("openToMakeCalls()", e);
- // TODO: how to send the exception back
- }
- }
-
- @Override
- public synchronized void open3(SipProfile localProfile,
- PendingIntent incomingCallPendingIntent,
- ISipSessionListener listener) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- localProfile.setCallingUid(Binder.getCallingUid());
- if (incomingCallPendingIntent == null) {
- if (DBG) log("open3: incomingCallPendingIntent cannot be null; "
- + "the profile is not opened");
- return;
- }
- if (DBG) log("open3: " + localProfile.getUriString() + ": "
- + incomingCallPendingIntent + ": " + listener);
- try {
- SipSessionGroupExt group = createGroup(localProfile,
- incomingCallPendingIntent, listener);
- if (localProfile.getAutoRegistration()) {
- group.openToReceiveCalls();
- updateWakeLocks();
- }
- } catch (SipException e) {
- loge("open3:", e);
- // TODO: how to send the exception back
- }
- }
-
- private boolean isCallerCreator(SipSessionGroupExt group) {
- SipProfile profile = group.getLocalProfile();
- return (profile.getCallingUid() == Binder.getCallingUid());
- }
-
- private boolean isCallerCreatorOrRadio(SipSessionGroupExt group) {
- return (isCallerRadio() || isCallerCreator(group));
- }
-
- private boolean isCallerRadio() {
- return (Binder.getCallingUid() == Process.PHONE_UID);
- }
-
- @Override
- public synchronized void close(String localProfileUri) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- SipSessionGroupExt group = mSipGroups.get(localProfileUri);
- if (group == null) return;
- if (!isCallerCreatorOrRadio(group)) {
- if (DBG) log("only creator or radio can close this profile");
- return;
- }
-
- group = mSipGroups.remove(localProfileUri);
- notifyProfileRemoved(group.getLocalProfile());
- group.close();
-
- updateWakeLocks();
- }
-
- @Override
- public synchronized boolean isOpened(String localProfileUri) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- SipSessionGroupExt group = mSipGroups.get(localProfileUri);
- if (group == null) return false;
- if (isCallerCreatorOrRadio(group)) {
- return true;
- } else {
- if (DBG) log("only creator or radio can query on the profile");
- return false;
- }
- }
-
- @Override
- public synchronized boolean isRegistered(String localProfileUri) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- SipSessionGroupExt group = mSipGroups.get(localProfileUri);
- if (group == null) return false;
- if (isCallerCreatorOrRadio(group)) {
- return group.isRegistered();
- } else {
- if (DBG) log("only creator or radio can query on the profile");
- return false;
- }
- }
-
- @Override
- public synchronized void setRegistrationListener(String localProfileUri,
- ISipSessionListener listener) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- SipSessionGroupExt group = mSipGroups.get(localProfileUri);
- if (group == null) return;
- if (isCallerCreator(group)) {
- group.setListener(listener);
- } else {
- if (DBG) log("only creator can set listener on the profile");
- }
- }
-
- @Override
- public synchronized ISipSession createSession(SipProfile localProfile,
- ISipSessionListener listener) {
- if (DBG) log("createSession: profile" + localProfile);
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- localProfile.setCallingUid(Binder.getCallingUid());
- if (mNetworkType == -1) {
- if (DBG) log("createSession: mNetworkType==-1 ret=null");
- return null;
- }
- try {
- SipSessionGroupExt group = createGroup(localProfile);
- return group.createSession(listener);
- } catch (SipException e) {
- if (DBG) loge("createSession;", e);
- return null;
- }
- }
-
- @Override
- public synchronized ISipSession getPendingSession(String callId) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- if (callId == null) return null;
- return mPendingSessions.get(callId);
- }
-
- private String determineLocalIp() {
- try {
- DatagramSocket s = new DatagramSocket();
- s.connect(InetAddress.getByName("192.168.1.1"), 80);
- return s.getLocalAddress().getHostAddress();
- } catch (IOException e) {
- if (DBG) loge("determineLocalIp()", e);
- // dont do anything; there should be a connectivity change going
- return null;
- }
- }
-
- private SipSessionGroupExt createGroup(SipProfile localProfile)
- throws SipException {
- String key = localProfile.getUriString();
- SipSessionGroupExt group = mSipGroups.get(key);
- if (group == null) {
- group = new SipSessionGroupExt(localProfile, null, null);
- mSipGroups.put(key, group);
- notifyProfileAdded(localProfile);
- } else if (!isCallerCreator(group)) {
- throw new SipException("only creator can access the profile");
- }
- return group;
- }
-
- private SipSessionGroupExt createGroup(SipProfile localProfile,
- PendingIntent incomingCallPendingIntent,
- ISipSessionListener listener) throws SipException {
- String key = localProfile.getUriString();
- SipSessionGroupExt group = mSipGroups.get(key);
- if (group != null) {
- if (!isCallerCreator(group)) {
- throw new SipException("only creator can access the profile");
- }
- group.setIncomingCallPendingIntent(incomingCallPendingIntent);
- group.setListener(listener);
- } else {
- group = new SipSessionGroupExt(localProfile,
- incomingCallPendingIntent, listener);
- mSipGroups.put(key, group);
- notifyProfileAdded(localProfile);
- }
- return group;
- }
-
- private void notifyProfileAdded(SipProfile localProfile) {
- if (DBG) log("notify: profile added: " + localProfile);
- Intent intent = new Intent(SipManager.ACTION_SIP_ADD_PHONE);
- intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
- mContext.sendBroadcast(intent);
- if (mSipGroups.size() == 1) {
- registerReceivers();
- }
- }
-
- private void notifyProfileRemoved(SipProfile localProfile) {
- if (DBG) log("notify: profile removed: " + localProfile);
- Intent intent = new Intent(SipManager.ACTION_SIP_REMOVE_PHONE);
- intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
- mContext.sendBroadcast(intent);
- if (mSipGroups.size() == 0) {
- unregisterReceivers();
- }
- }
-
- private void stopPortMappingMeasurement() {
- if (mSipKeepAliveProcessCallback != null) {
- mSipKeepAliveProcessCallback.stop();
- mSipKeepAliveProcessCallback = null;
- }
- }
-
- private void startPortMappingLifetimeMeasurement(
- SipProfile localProfile) {
- startPortMappingLifetimeMeasurement(localProfile,
- DEFAULT_MAX_KEEPALIVE_INTERVAL);
- }
-
- private void startPortMappingLifetimeMeasurement(
- SipProfile localProfile, int maxInterval) {
- if ((mSipKeepAliveProcessCallback == null)
- && (mKeepAliveInterval == -1)
- && isBehindNAT(mLocalIp)) {
- if (DBG) log("startPortMappingLifetimeMeasurement: profile="
- + localProfile.getUriString());
-
- int minInterval = mLastGoodKeepAliveInterval;
- if (minInterval >= maxInterval) {
- // If mLastGoodKeepAliveInterval also does not work, reset it
- // to the default min
- minInterval = mLastGoodKeepAliveInterval
- = DEFAULT_KEEPALIVE_INTERVAL;
- log(" reset min interval to " + minInterval);
- }
- mSipKeepAliveProcessCallback = new SipKeepAliveProcessCallback(
- localProfile, minInterval, maxInterval);
- mSipKeepAliveProcessCallback.start();
- }
- }
-
- private void restartPortMappingLifetimeMeasurement(
- SipProfile localProfile, int maxInterval) {
- stopPortMappingMeasurement();
- mKeepAliveInterval = -1;
- startPortMappingLifetimeMeasurement(localProfile, maxInterval);
- }
-
- private synchronized void addPendingSession(ISipSession session) {
- try {
- cleanUpPendingSessions();
- mPendingSessions.put(session.getCallId(), session);
- if (DBG) log("#pending sess=" + mPendingSessions.size());
- } catch (RemoteException e) {
- // should not happen with a local call
- loge("addPendingSession()", e);
- }
- }
-
- private void cleanUpPendingSessions() throws RemoteException {
- Map.Entry<String, ISipSession>[] entries =
- mPendingSessions.entrySet().toArray(
- new Map.Entry[mPendingSessions.size()]);
- for (Map.Entry<String, ISipSession> entry : entries) {
- if (entry.getValue().getState() != SipSession.State.INCOMING_CALL) {
- mPendingSessions.remove(entry.getKey());
- }
- }
- }
-
- private synchronized boolean callingSelf(SipSessionGroupExt ringingGroup,
- SipSessionGroup.SipSessionImpl ringingSession) {
- String callId = ringingSession.getCallId();
- for (SipSessionGroupExt group : mSipGroups.values()) {
- if ((group != ringingGroup) && group.containsSession(callId)) {
- if (DBG) log("call self: "
- + ringingSession.getLocalProfile().getUriString()
- + " -> " + group.getLocalProfile().getUriString());
- return true;
- }
- }
- return false;
- }
-
- private synchronized void onKeepAliveIntervalChanged() {
- for (SipSessionGroupExt group : mSipGroups.values()) {
- group.onKeepAliveIntervalChanged();
- }
- }
-
- private int getKeepAliveInterval() {
- return (mKeepAliveInterval < 0)
- ? mLastGoodKeepAliveInterval
- : mKeepAliveInterval;
- }
-
- private boolean isBehindNAT(String address) {
- try {
- // TODO: How is isBehindNAT used and why these constanst address:
- // 10.x.x.x | 192.168.x.x | 172.16.x.x .. 172.19.x.x
- byte[] d = InetAddress.getByName(address).getAddress();
- if ((d[0] == 10) ||
- (((0x000000FF & d[0]) == 172) &&
- ((0x000000F0 & d[1]) == 16)) ||
- (((0x000000FF & d[0]) == 192) &&
- ((0x000000FF & d[1]) == 168))) {
- return true;
- }
- } catch (UnknownHostException e) {
- loge("isBehindAT()" + address, e);
- }
- return false;
- }
-
- private class SipSessionGroupExt extends SipSessionAdapter {
- private static final String SSGE_TAG = "SipSessionGroupExt";
- private static final boolean SSGE_DBG = true;
- private SipSessionGroup mSipGroup;
- private PendingIntent mIncomingCallPendingIntent;
- private boolean mOpenedToReceiveCalls;
-
- private SipAutoReg mAutoRegistration =
- new SipAutoReg();
-
- public SipSessionGroupExt(SipProfile localProfile,
- PendingIntent incomingCallPendingIntent,
- ISipSessionListener listener) throws SipException {
- if (SSGE_DBG) log("SipSessionGroupExt: profile=" + localProfile);
- mSipGroup = new SipSessionGroup(duplicate(localProfile),
- localProfile.getPassword(), mTimer, mMyWakeLock);
- mIncomingCallPendingIntent = incomingCallPendingIntent;
- mAutoRegistration.setListener(listener);
- }
-
- public SipProfile getLocalProfile() {
- return mSipGroup.getLocalProfile();
- }
-
- public boolean containsSession(String callId) {
- return mSipGroup.containsSession(callId);
- }
-
- public void onKeepAliveIntervalChanged() {
- mAutoRegistration.onKeepAliveIntervalChanged();
- }
-
- // TODO: remove this method once SipWakeupTimer can better handle variety
- // of timeout values
- void setWakeupTimer(SipWakeupTimer timer) {
- mSipGroup.setWakeupTimer(timer);
- }
-
- private SipProfile duplicate(SipProfile p) {
- try {
- return new SipProfile.Builder(p).setPassword("*").build();
- } catch (Exception e) {
- loge("duplicate()", e);
- throw new RuntimeException("duplicate profile", e);
- }
- }
-
- public void setListener(ISipSessionListener listener) {
- mAutoRegistration.setListener(listener);
- }
-
- public void setIncomingCallPendingIntent(PendingIntent pIntent) {
- mIncomingCallPendingIntent = pIntent;
- }
-
- public void openToReceiveCalls() throws SipException {
- mOpenedToReceiveCalls = true;
- if (mNetworkType != -1) {
- mSipGroup.openToReceiveCalls(this);
- mAutoRegistration.start(mSipGroup);
- }
- if (SSGE_DBG) log("openToReceiveCalls: " + getUri() + ": "
- + mIncomingCallPendingIntent);
- }
-
- public void onConnectivityChanged(boolean connected)
- throws SipException {
- if (SSGE_DBG) {
- log("onConnectivityChanged: connected=" + connected + " uri="
- + getUri() + ": " + mIncomingCallPendingIntent);
- }
- mSipGroup.onConnectivityChanged();
- if (connected) {
- mSipGroup.reset();
- if (mOpenedToReceiveCalls) openToReceiveCalls();
- } else {
- mSipGroup.close();
- mAutoRegistration.stop();
- }
- }
-
- public void close() {
- mOpenedToReceiveCalls = false;
- mSipGroup.close();
- mAutoRegistration.stop();
- if (SSGE_DBG) log("close: " + getUri() + ": " + mIncomingCallPendingIntent);
- }
-
- public ISipSession createSession(ISipSessionListener listener) {
- if (SSGE_DBG) log("createSession");
- return mSipGroup.createSession(listener);
- }
-
- @Override
- public void onRinging(ISipSession s, SipProfile caller,
- String sessionDescription) {
- SipSessionGroup.SipSessionImpl session =
- (SipSessionGroup.SipSessionImpl) s;
- synchronized (SipService.this) {
- try {
- if (!isRegistered() || callingSelf(this, session)) {
- if (SSGE_DBG) log("onRinging: end notReg or self");
- session.endCall();
- return;
- }
-
- // send out incoming call broadcast
- addPendingSession(session);
- Intent intent = SipManager.createIncomingCallBroadcast(
- session.getCallId(), sessionDescription);
- if (SSGE_DBG) log("onRinging: uri=" + getUri() + ": "
- + caller.getUri() + ": " + session.getCallId()
- + " " + mIncomingCallPendingIntent);
- mIncomingCallPendingIntent.send(mContext,
- SipManager.INCOMING_CALL_RESULT_CODE, intent);
- } catch (PendingIntent.CanceledException e) {
- loge("onRinging: pendingIntent is canceled, drop incoming call", e);
- session.endCall();
- }
- }
- }
-
- @Override
- public void onError(ISipSession session, int errorCode,
- String message) {
- if (SSGE_DBG) log("onError: errorCode=" + errorCode + " desc="
- + SipErrorCode.toString(errorCode) + ": " + message);
- }
-
- public boolean isOpenedToReceiveCalls() {
- return mOpenedToReceiveCalls;
- }
-
- public boolean isRegistered() {
- return mAutoRegistration.isRegistered();
- }
-
- private String getUri() {
- return mSipGroup.getLocalProfileUri();
- }
-
- private void log(String s) {
- Rlog.d(SSGE_TAG, s);
- }
-
- private void loge(String s, Throwable t) {
- Rlog.e(SSGE_TAG, s, t);
- }
-
- }
-
- private class SipKeepAliveProcessCallback implements Runnable,
- SipSessionGroup.KeepAliveProcessCallback {
- private static final String SKAI_TAG = "SipKeepAliveProcessCallback";
- private static final boolean SKAI_DBG = true;
- private static final int MIN_INTERVAL = 5; // in seconds
- private static final int PASS_THRESHOLD = 10;
- private static final int NAT_MEASUREMENT_RETRY_INTERVAL = 120; // in seconds
- private SipProfile mLocalProfile;
- private SipSessionGroupExt mGroup;
- private SipSessionGroup.SipSessionImpl mSession;
- private int mMinInterval;
- private int mMaxInterval;
- private int mInterval;
- private int mPassCount;
-
- public SipKeepAliveProcessCallback(SipProfile localProfile,
- int minInterval, int maxInterval) {
- mMaxInterval = maxInterval;
- mMinInterval = minInterval;
- mLocalProfile = localProfile;
- }
-
- public void start() {
- synchronized (SipService.this) {
- if (mSession != null) {
- return;
- }
-
- mInterval = (mMaxInterval + mMinInterval) / 2;
- mPassCount = 0;
-
- // Don't start measurement if the interval is too small
- if (mInterval < DEFAULT_KEEPALIVE_INTERVAL || checkTermination()) {
- if (SKAI_DBG) log("start: measurement aborted; interval=[" +
- mMinInterval + "," + mMaxInterval + "]");
- return;
- }
-
- try {
- if (SKAI_DBG) log("start: interval=" + mInterval);
-
- mGroup = new SipSessionGroupExt(mLocalProfile, null, null);
- // TODO: remove this line once SipWakeupTimer can better handle
- // variety of timeout values
- mGroup.setWakeupTimer(new SipWakeupTimer(mContext, mExecutor));
-
- mSession = (SipSessionGroup.SipSessionImpl)
- mGroup.createSession(null);
- mSession.startKeepAliveProcess(mInterval, this);
- } catch (Throwable t) {
- onError(SipErrorCode.CLIENT_ERROR, t.toString());
- }
- }
- }
-
- public void stop() {
- synchronized (SipService.this) {
- if (mSession != null) {
- mSession.stopKeepAliveProcess();
- mSession = null;
- }
- if (mGroup != null) {
- mGroup.close();
- mGroup = null;
- }
- mTimer.cancel(this);
- if (SKAI_DBG) log("stop");
- }
- }
-
- private void restart() {
- synchronized (SipService.this) {
- // Return immediately if the measurement process is stopped
- if (mSession == null) return;
-
- if (SKAI_DBG) log("restart: interval=" + mInterval);
- try {
- mSession.stopKeepAliveProcess();
- mPassCount = 0;
- mSession.startKeepAliveProcess(mInterval, this);
- } catch (SipException e) {
- loge("restart", e);
- }
- }
- }
-
- private boolean checkTermination() {
- return ((mMaxInterval - mMinInterval) < MIN_INTERVAL);
- }
-
- // SipSessionGroup.KeepAliveProcessCallback
- @Override
- public void onResponse(boolean portChanged) {
- synchronized (SipService.this) {
- if (!portChanged) {
- if (++mPassCount != PASS_THRESHOLD) return;
- // update the interval, since the current interval is good to
- // keep the port mapping.
- if (mKeepAliveInterval > 0) {
- mLastGoodKeepAliveInterval = mKeepAliveInterval;
- }
- mKeepAliveInterval = mMinInterval = mInterval;
- if (SKAI_DBG) {
- log("onResponse: portChanged=" + portChanged + " mKeepAliveInterval="
- + mKeepAliveInterval);
- }
- onKeepAliveIntervalChanged();
- } else {
- // Since the rport is changed, shorten the interval.
- mMaxInterval = mInterval;
- }
- if (checkTermination()) {
- // update mKeepAliveInterval and stop measurement.
- stop();
- // If all the measurements failed, we still set it to
- // mMinInterval; If mMinInterval still doesn't work, a new
- // measurement with min interval=DEFAULT_KEEPALIVE_INTERVAL
- // will be conducted.
- mKeepAliveInterval = mMinInterval;
- if (SKAI_DBG) {
- log("onResponse: checkTermination mKeepAliveInterval="
- + mKeepAliveInterval);
- }
- } else {
- // calculate the new interval and continue.
- mInterval = (mMaxInterval + mMinInterval) / 2;
- if (SKAI_DBG) {
- log("onResponse: mKeepAliveInterval=" + mKeepAliveInterval
- + ", new mInterval=" + mInterval);
- }
- restart();
- }
- }
- }
-
- // SipSessionGroup.KeepAliveProcessCallback
- @Override
- public void onError(int errorCode, String description) {
- if (SKAI_DBG) loge("onError: errorCode=" + errorCode + " desc=" + description);
- restartLater();
- }
-
- // timeout handler
- @Override
- public void run() {
- mTimer.cancel(this);
- restart();
- }
-
- private void restartLater() {
- synchronized (SipService.this) {
- int interval = NAT_MEASUREMENT_RETRY_INTERVAL;
- mTimer.cancel(this);
- mTimer.set(interval * 1000, this);
- }
- }
-
- private void log(String s) {
- Rlog.d(SKAI_TAG, s);
- }
-
- private void loge(String s) {
- Rlog.d(SKAI_TAG, s);
- }
-
- private void loge(String s, Throwable t) {
- Rlog.d(SKAI_TAG, s, t);
- }
- }
-
- private class SipAutoReg extends SipSessionAdapter
- implements Runnable, SipSessionGroup.KeepAliveProcessCallback {
- private String SAR_TAG;
- private static final boolean SAR_DBG = true;
- private static final int MIN_KEEPALIVE_SUCCESS_COUNT = 10;
-
- private SipSessionGroup.SipSessionImpl mSession;
- private SipSessionGroup.SipSessionImpl mKeepAliveSession;
- private SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
- private int mBackoff = 1;
- private boolean mRegistered;
- private long mExpiryTime;
- private int mErrorCode;
- private String mErrorMessage;
- private boolean mRunning = false;
-
- private int mKeepAliveSuccessCount = 0;
-
- public void start(SipSessionGroup group) {
- if (!mRunning) {
- mRunning = true;
- mBackoff = 1;
- mSession = (SipSessionGroup.SipSessionImpl)
- group.createSession(this);
- // return right away if no active network connection.
- if (mSession == null) return;
-
- // start unregistration to clear up old registration at server
- // TODO: when rfc5626 is deployed, use reg-id and sip.instance
- // in registration to avoid adding duplicate entries to server
- mMyWakeLock.acquire(mSession);
- mSession.unregister();
- SAR_TAG = "SipAutoReg:" + mSession.getLocalProfile().getUriString();
- if (SAR_DBG) log("start: group=" + group);
- }
- }
-
- private void startKeepAliveProcess(int interval) {
- if (SAR_DBG) log("startKeepAliveProcess: interval=" + interval);
- if (mKeepAliveSession == null) {
- mKeepAliveSession = mSession.duplicate();
- } else {
- mKeepAliveSession.stopKeepAliveProcess();
- }
- try {
- mKeepAliveSession.startKeepAliveProcess(interval, this);
- } catch (SipException e) {
- loge("startKeepAliveProcess: interval=" + interval, e);
- }
- }
-
- private void stopKeepAliveProcess() {
- if (mKeepAliveSession != null) {
- mKeepAliveSession.stopKeepAliveProcess();
- mKeepAliveSession = null;
- }
- mKeepAliveSuccessCount = 0;
- }
-
- // SipSessionGroup.KeepAliveProcessCallback
- @Override
- public void onResponse(boolean portChanged) {
- synchronized (SipService.this) {
- if (portChanged) {
- int interval = getKeepAliveInterval();
- if (mKeepAliveSuccessCount < MIN_KEEPALIVE_SUCCESS_COUNT) {
- if (SAR_DBG) {
- log("onResponse: keepalive doesn't work with interval "
- + interval + ", past success count="
- + mKeepAliveSuccessCount);
- }
- if (interval > DEFAULT_KEEPALIVE_INTERVAL) {
- restartPortMappingLifetimeMeasurement(
- mSession.getLocalProfile(), interval);
- mKeepAliveSuccessCount = 0;
- }
- } else {
- if (SAR_DBG) {
- log("keep keepalive going with interval "
- + interval + ", past success count="
- + mKeepAliveSuccessCount);
- }
- mKeepAliveSuccessCount /= 2;
- }
- } else {
- // Start keep-alive interval measurement on the first
- // successfully kept-alive SipSessionGroup
- startPortMappingLifetimeMeasurement(
- mSession.getLocalProfile());
- mKeepAliveSuccessCount++;
- }
-
- if (!mRunning || !portChanged) return;
-
- // The keep alive process is stopped when port is changed;
- // Nullify the session so that the process can be restarted
- // again when the re-registration is done
- mKeepAliveSession = null;
-
- // Acquire wake lock for the registration process. The
- // lock will be released when registration is complete.
- mMyWakeLock.acquire(mSession);
- mSession.register(EXPIRY_TIME);
- }
- }
-
- // SipSessionGroup.KeepAliveProcessCallback
- @Override
- public void onError(int errorCode, String description) {
- if (SAR_DBG) {
- loge("onError: errorCode=" + errorCode + " desc=" + description);
- }
- onResponse(true); // re-register immediately
- }
-
- public void stop() {
- if (!mRunning) return;
- mRunning = false;
- mMyWakeLock.release(mSession);
- if (mSession != null) {
- mSession.setListener(null);
- if (mNetworkType != -1 && mRegistered) mSession.unregister();
- }
-
- mTimer.cancel(this);
- stopKeepAliveProcess();
-
- mRegistered = false;
- setListener(mProxy.getListener());
- }
-
- public void onKeepAliveIntervalChanged() {
- if (mKeepAliveSession != null) {
- int newInterval = getKeepAliveInterval();
- if (SAR_DBG) {
- log("onKeepAliveIntervalChanged: interval=" + newInterval);
- }
- mKeepAliveSuccessCount = 0;
- startKeepAliveProcess(newInterval);
- }
- }
-
- public void setListener(ISipSessionListener listener) {
- synchronized (SipService.this) {
- mProxy.setListener(listener);
-
- try {
- int state = (mSession == null)
- ? SipSession.State.READY_TO_CALL
- : mSession.getState();
- if ((state == SipSession.State.REGISTERING)
- || (state == SipSession.State.DEREGISTERING)) {
- mProxy.onRegistering(mSession);
- } else if (mRegistered) {
- int duration = (int)
- (mExpiryTime - SystemClock.elapsedRealtime());
- mProxy.onRegistrationDone(mSession, duration);
- } else if (mErrorCode != SipErrorCode.NO_ERROR) {
- if (mErrorCode == SipErrorCode.TIME_OUT) {
- mProxy.onRegistrationTimeout(mSession);
- } else {
- mProxy.onRegistrationFailed(mSession, mErrorCode,
- mErrorMessage);
- }
- } else if (mNetworkType == -1) {
- mProxy.onRegistrationFailed(mSession,
- SipErrorCode.DATA_CONNECTION_LOST,
- "no data connection");
- } else if (!mRunning) {
- mProxy.onRegistrationFailed(mSession,
- SipErrorCode.CLIENT_ERROR,
- "registration not running");
- } else {
- mProxy.onRegistrationFailed(mSession,
- SipErrorCode.IN_PROGRESS,
- String.valueOf(state));
- }
- } catch (Throwable t) {
- loge("setListener: ", t);
- }
- }
- }
-
- public boolean isRegistered() {
- return mRegistered;
- }
-
- // timeout handler: re-register
- @Override
- public void run() {
- synchronized (SipService.this) {
- if (!mRunning) return;
-
- mErrorCode = SipErrorCode.NO_ERROR;
- mErrorMessage = null;
- if (SAR_DBG) log("run: registering");
- if (mNetworkType != -1) {
- mMyWakeLock.acquire(mSession);
- mSession.register(EXPIRY_TIME);
- }
- }
- }
-
- private void restart(int duration) {
- if (SAR_DBG) log("restart: duration=" + duration + "s later.");
- mTimer.cancel(this);
- mTimer.set(duration * 1000, this);
- }
-
- private int backoffDuration() {
- int duration = SHORT_EXPIRY_TIME * mBackoff;
- if (duration > 3600) {
- duration = 3600;
- } else {
- mBackoff *= 2;
- }
- return duration;
- }
-
- @Override
- public void onRegistering(ISipSession session) {
- if (SAR_DBG) log("onRegistering: " + session);
- synchronized (SipService.this) {
- if (notCurrentSession(session)) return;
-
- mRegistered = false;
- mProxy.onRegistering(session);
- }
- }
-
- private boolean notCurrentSession(ISipSession session) {
- if (session != mSession) {
- ((SipSessionGroup.SipSessionImpl) session).setListener(null);
- mMyWakeLock.release(session);
- return true;
- }
- return !mRunning;
- }
-
- @Override
- public void onRegistrationDone(ISipSession session, int duration) {
- if (SAR_DBG) log("onRegistrationDone: " + session);
- synchronized (SipService.this) {
- if (notCurrentSession(session)) return;
-
- mProxy.onRegistrationDone(session, duration);
-
- if (duration > 0) {
- mExpiryTime = SystemClock.elapsedRealtime()
- + (duration * 1000);
-
- if (!mRegistered) {
- mRegistered = true;
- // allow some overlap to avoid call drop during renew
- duration -= MIN_EXPIRY_TIME;
- if (duration < MIN_EXPIRY_TIME) {
- duration = MIN_EXPIRY_TIME;
- }
- restart(duration);
-
- SipProfile localProfile = mSession.getLocalProfile();
- if ((mKeepAliveSession == null) && (isBehindNAT(mLocalIp)
- || localProfile.getSendKeepAlive())) {
- startKeepAliveProcess(getKeepAliveInterval());
- }
- }
- mMyWakeLock.release(session);
- } else {
- mRegistered = false;
- mExpiryTime = -1L;
- if (SAR_DBG) log("Refresh registration immediately");
- run();
- }
- }
- }
-
- @Override
- public void onRegistrationFailed(ISipSession session, int errorCode,
- String message) {
- if (SAR_DBG) log("onRegistrationFailed: " + session + ": "
- + SipErrorCode.toString(errorCode) + ": " + message);
- synchronized (SipService.this) {
- if (notCurrentSession(session)) return;
-
- switch (errorCode) {
- case SipErrorCode.INVALID_CREDENTIALS:
- case SipErrorCode.SERVER_UNREACHABLE:
- if (SAR_DBG) log(" pause auto-registration");
- stop();
- break;
- default:
- restartLater();
- }
-
- mErrorCode = errorCode;
- mErrorMessage = message;
- mProxy.onRegistrationFailed(session, errorCode, message);
- mMyWakeLock.release(session);
- }
- }
-
- @Override
- public void onRegistrationTimeout(ISipSession session) {
- if (SAR_DBG) log("onRegistrationTimeout: " + session);
- synchronized (SipService.this) {
- if (notCurrentSession(session)) return;
-
- mErrorCode = SipErrorCode.TIME_OUT;
- mProxy.onRegistrationTimeout(session);
- restartLater();
- mMyWakeLock.release(session);
- }
- }
-
- private void restartLater() {
- if (SAR_DBG) loge("restartLater");
- mRegistered = false;
- restart(backoffDuration());
- }
-
- private void log(String s) {
- Rlog.d(SAR_TAG, s);
- }
-
- private void loge(String s) {
- Rlog.e(SAR_TAG, s);
- }
-
- private void loge(String s, Throwable e) {
- Rlog.e(SAR_TAG, s, e);
- }
- }
-
- private class ConnectivityReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Bundle bundle = intent.getExtras();
- if (bundle != null) {
- final NetworkInfo info = (NetworkInfo)
- bundle.get(ConnectivityManager.EXTRA_NETWORK_INFO);
-
- // Run the handler in MyExecutor to be protected by wake lock
- mExecutor.execute(new Runnable() {
- @Override
- public void run() {
- onConnectivityChanged(info);
- }
- });
- }
- }
- }
-
- private void registerReceivers() {
- mContext.registerReceiver(mConnectivityReceiver,
- new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
- if (DBG) log("registerReceivers:");
- }
-
- private void unregisterReceivers() {
- mContext.unregisterReceiver(mConnectivityReceiver);
- if (DBG) log("unregisterReceivers:");
-
- // Reset variables maintained by ConnectivityReceiver.
- mWifiLock.release();
- mNetworkType = -1;
- }
-
- private void updateWakeLocks() {
- for (SipSessionGroupExt group : mSipGroups.values()) {
- if (group.isOpenedToReceiveCalls()) {
- // Also grab the WifiLock when we are disconnected, so the
- // system will keep trying to reconnect. It will be released
- // when the system eventually connects to something else.
- if (mNetworkType == ConnectivityManager.TYPE_WIFI || mNetworkType == -1) {
- mWifiLock.acquire();
- } else {
- mWifiLock.release();
- }
- return;
- }
- }
- mWifiLock.release();
- mMyWakeLock.reset(); // in case there's a leak
- }
-
- private synchronized void onConnectivityChanged(NetworkInfo info) {
- // We only care about the default network, and getActiveNetworkInfo()
- // is the only way to distinguish them. However, as broadcasts are
- // delivered asynchronously, we might miss DISCONNECTED events from
- // getActiveNetworkInfo(), which is critical to our SIP stack. To
- // solve this, if it is a DISCONNECTED event to our current network,
- // respect it. Otherwise get a new one from getActiveNetworkInfo().
- if (info == null || info.isConnected() || info.getType() != mNetworkType) {
- ConnectivityManager cm = (ConnectivityManager)
- mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- info = cm.getActiveNetworkInfo();
- }
-
- // Some devices limit SIP on Wi-Fi. In this case, if we are not on
- // Wi-Fi, treat it as a DISCONNECTED event.
- int networkType = (info != null && info.isConnected()) ? info.getType() : -1;
- if (mSipOnWifiOnly && networkType != ConnectivityManager.TYPE_WIFI) {
- networkType = -1;
- }
-
- // Ignore the event if the current active network is not changed.
- if (mNetworkType == networkType) {
- // TODO: Maybe we need to send seq/generation number
- return;
- }
- if (DBG) {
- log("onConnectivityChanged: " + mNetworkType +
- " -> " + networkType);
- }
-
- try {
- if (mNetworkType != -1) {
- mLocalIp = null;
- stopPortMappingMeasurement();
- for (SipSessionGroupExt group : mSipGroups.values()) {
- group.onConnectivityChanged(false);
- }
- }
- mNetworkType = networkType;
-
- if (mNetworkType != -1) {
- mLocalIp = determineLocalIp();
- mKeepAliveInterval = -1;
- mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
- for (SipSessionGroupExt group : mSipGroups.values()) {
- group.onConnectivityChanged(true);
- }
- }
- updateWakeLocks();
- } catch (SipException e) {
- loge("onConnectivityChanged()", e);
- }
- }
-
- private static Looper createLooper() {
- HandlerThread thread = new HandlerThread("SipService.Executor");
- thread.start();
- return thread.getLooper();
- }
-
- // Executes immediate tasks in a single thread.
- // Hold/release wake lock for running tasks
- private class MyExecutor extends Handler implements Executor {
- MyExecutor() {
- super(createLooper());
- }
-
- @Override
- public void execute(Runnable task) {
- mMyWakeLock.acquire(task);
- Message.obtain(this, 0/* don't care */, task).sendToTarget();
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (msg.obj instanceof Runnable) {
- executeInternal((Runnable) msg.obj);
- } else {
- if (DBG) log("handleMessage: not Runnable ignore msg=" + msg);
- }
- }
-
- private void executeInternal(Runnable task) {
- try {
- task.run();
- } catch (Throwable t) {
- loge("run task: " + task, t);
- } finally {
- mMyWakeLock.release(task);
- }
- }
- }
-
- private void log(String s) {
- Rlog.d(TAG, s);
- }
-
- private static void slog(String s) {
- Rlog.d(TAG, s);
- }
-
- private void loge(String s, Throwable e) {
- Rlog.e(TAG, s, e);
- }
-}
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
deleted file mode 100644
index e820f35..0000000
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ /dev/null
@@ -1,1863 +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 com.android.server.sip;
-
-import gov.nist.javax.sip.clientauthutils.AccountManager;
-import gov.nist.javax.sip.clientauthutils.UserCredentials;
-import gov.nist.javax.sip.header.ProxyAuthenticate;
-import gov.nist.javax.sip.header.ReferTo;
-import gov.nist.javax.sip.header.SIPHeaderNames;
-import gov.nist.javax.sip.header.StatusLine;
-import gov.nist.javax.sip.header.WWWAuthenticate;
-import gov.nist.javax.sip.header.extensions.ReferredByHeader;
-import gov.nist.javax.sip.header.extensions.ReplacesHeader;
-import gov.nist.javax.sip.message.SIPMessage;
-import gov.nist.javax.sip.message.SIPResponse;
-
-import android.net.sip.ISipSession;
-import android.net.sip.ISipSessionListener;
-import android.net.sip.SipErrorCode;
-import android.net.sip.SipProfile;
-import android.net.sip.SipSession;
-import android.net.sip.SipSessionAdapter;
-import android.text.TextUtils;
-import android.telephony.Rlog;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.DatagramSocket;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.text.ParseException;
-import java.util.EventObject;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-
-import javax.sip.ClientTransaction;
-import javax.sip.Dialog;
-import javax.sip.DialogTerminatedEvent;
-import javax.sip.IOExceptionEvent;
-import javax.sip.ObjectInUseException;
-import javax.sip.RequestEvent;
-import javax.sip.ResponseEvent;
-import javax.sip.ServerTransaction;
-import javax.sip.SipException;
-import javax.sip.SipFactory;
-import javax.sip.SipListener;
-import javax.sip.SipProvider;
-import javax.sip.SipStack;
-import javax.sip.TimeoutEvent;
-import javax.sip.Transaction;
-import javax.sip.TransactionTerminatedEvent;
-import javax.sip.address.Address;
-import javax.sip.address.SipURI;
-import javax.sip.header.CSeqHeader;
-import javax.sip.header.ContactHeader;
-import javax.sip.header.ExpiresHeader;
-import javax.sip.header.FromHeader;
-import javax.sip.header.HeaderAddress;
-import javax.sip.header.MinExpiresHeader;
-import javax.sip.header.ReferToHeader;
-import javax.sip.header.ViaHeader;
-import javax.sip.message.Message;
-import javax.sip.message.Request;
-import javax.sip.message.Response;
-
-
-/**
- * Manages {@link ISipSession}'s for a SIP account.
- */
-class SipSessionGroup implements SipListener {
- private static final String TAG = "SipSession";
- private static final boolean DBG = false;
- private static final boolean DBG_PING = false;
- private static final String ANONYMOUS = "anonymous";
- // Limit the size of thread pool to 1 for the order issue when the phone is
- // waken up from sleep and there are many packets to be processed in the SIP
- // stack. Note: The default thread pool size in NIST SIP stack is -1 which is
- // unlimited.
- private static final String THREAD_POOL_SIZE = "1";
- private static final int EXPIRY_TIME = 3600; // in seconds
- private static final int CANCEL_CALL_TIMER = 3; // in seconds
- private static final int END_CALL_TIMER = 3; // in seconds
- private static final int KEEPALIVE_TIMEOUT = 5; // in seconds
- private static final int INCALL_KEEPALIVE_INTERVAL = 10; // in seconds
- private static final long WAKE_LOCK_HOLDING_TIME = 500; // in milliseconds
-
- private static final EventObject DEREGISTER = new EventObject("Deregister");
- private static final EventObject END_CALL = new EventObject("End call");
-
- private final SipProfile mLocalProfile;
- private final String mPassword;
-
- private SipStack mSipStack;
- private SipHelper mSipHelper;
-
- // session that processes INVITE requests
- private SipSessionImpl mCallReceiverSession;
- private String mLocalIp;
-
- private SipWakeupTimer mWakeupTimer;
- private SipWakeLock mWakeLock;
-
- // call-id-to-SipSession map
- private Map<String, SipSessionImpl> mSessionMap =
- new HashMap<String, SipSessionImpl>();
-
- // external address observed from any response
- private String mExternalIp;
- private int mExternalPort;
-
- /**
- * @param profile the local profile with password crossed out
- * @param password the password of the profile
- * @throws SipException if cannot assign requested address
- */
- public SipSessionGroup(SipProfile profile, String password,
- SipWakeupTimer timer, SipWakeLock wakeLock) throws SipException {
- mLocalProfile = profile;
- mPassword = password;
- mWakeupTimer = timer;
- mWakeLock = wakeLock;
- reset();
- }
-
- // TODO: remove this method once SipWakeupTimer can better handle variety
- // of timeout values
- void setWakeupTimer(SipWakeupTimer timer) {
- mWakeupTimer = timer;
- }
-
- synchronized void reset() throws SipException {
- Properties properties = new Properties();
-
- String protocol = mLocalProfile.getProtocol();
- int port = mLocalProfile.getPort();
- String server = mLocalProfile.getProxyAddress();
-
- if (!TextUtils.isEmpty(server)) {
- properties.setProperty("javax.sip.OUTBOUND_PROXY",
- server + ':' + port + '/' + protocol);
- } else {
- server = mLocalProfile.getSipDomain();
- }
- if (server.startsWith("[") && server.endsWith("]")) {
- server = server.substring(1, server.length() - 1);
- }
-
- String local = null;
- try {
- for (InetAddress remote : InetAddress.getAllByName(server)) {
- DatagramSocket socket = new DatagramSocket();
- socket.connect(remote, port);
- if (socket.isConnected()) {
- local = socket.getLocalAddress().getHostAddress();
- port = socket.getLocalPort();
- socket.close();
- break;
- }
- socket.close();
- }
- } catch (Exception e) {
- // ignore.
- }
- if (local == null) {
- // We are unable to reach the server. Just bail out.
- return;
- }
-
- close();
- mLocalIp = local;
-
- properties.setProperty("javax.sip.STACK_NAME", getStackName());
- properties.setProperty(
- "gov.nist.javax.sip.THREAD_POOL_SIZE", THREAD_POOL_SIZE);
- mSipStack = SipFactory.getInstance().createSipStack(properties);
- try {
- SipProvider provider = mSipStack.createSipProvider(
- mSipStack.createListeningPoint(local, port, protocol));
- provider.addSipListener(this);
- mSipHelper = new SipHelper(mSipStack, provider);
- } catch (SipException e) {
- throw e;
- } catch (Exception e) {
- throw new SipException("failed to initialize SIP stack", e);
- }
-
- if (DBG) log("reset: start stack for " + mLocalProfile.getUriString());
- mSipStack.start();
- }
-
- synchronized void onConnectivityChanged() {
- SipSessionImpl[] ss = mSessionMap.values().toArray(
- new SipSessionImpl[mSessionMap.size()]);
- // Iterate on the copied array instead of directly on mSessionMap to
- // avoid ConcurrentModificationException being thrown when
- // SipSessionImpl removes itself from mSessionMap in onError() in the
- // following loop.
- for (SipSessionImpl s : ss) {
- s.onError(SipErrorCode.DATA_CONNECTION_LOST,
- "data connection lost");
- }
- }
-
- synchronized void resetExternalAddress() {
- if (DBG) {
- log("resetExternalAddress: " + mSipStack);
- }
- mExternalIp = null;
- mExternalPort = 0;
- }
-
- public SipProfile getLocalProfile() {
- return mLocalProfile;
- }
-
- public String getLocalProfileUri() {
- return mLocalProfile.getUriString();
- }
-
- private String getStackName() {
- return "stack" + System.currentTimeMillis();
- }
-
- public synchronized void close() {
- if (DBG) log("close: " + mLocalProfile.getUriString());
- onConnectivityChanged();
- mSessionMap.clear();
- closeToNotReceiveCalls();
- if (mSipStack != null) {
- mSipStack.stop();
- mSipStack = null;
- mSipHelper = null;
- }
- resetExternalAddress();
- }
-
- public synchronized boolean isClosed() {
- return (mSipStack == null);
- }
-
- // For internal use, require listener not to block in callbacks.
- public synchronized void openToReceiveCalls(ISipSessionListener listener) {
- if (mCallReceiverSession == null) {
- mCallReceiverSession = new SipSessionCallReceiverImpl(listener);
- } else {
- mCallReceiverSession.setListener(listener);
- }
- }
-
- public synchronized void closeToNotReceiveCalls() {
- mCallReceiverSession = null;
- }
-
- public ISipSession createSession(ISipSessionListener listener) {
- return (isClosed() ? null : new SipSessionImpl(listener));
- }
-
- synchronized boolean containsSession(String callId) {
- return mSessionMap.containsKey(callId);
- }
-
- private synchronized SipSessionImpl getSipSession(EventObject event) {
- String key = SipHelper.getCallId(event);
- SipSessionImpl session = mSessionMap.get(key);
- if ((session != null) && isLoggable(session)) {
- if (DBG) log("getSipSession: event=" + key);
- if (DBG) log("getSipSession: active sessions:");
- for (String k : mSessionMap.keySet()) {
- if (DBG) log("getSipSession: ..." + k + ": " + mSessionMap.get(k));
- }
- }
- return ((session != null) ? session : mCallReceiverSession);
- }
-
- private synchronized void addSipSession(SipSessionImpl newSession) {
- removeSipSession(newSession);
- String key = newSession.getCallId();
- mSessionMap.put(key, newSession);
- if (isLoggable(newSession)) {
- if (DBG) log("addSipSession: key='" + key + "'");
- for (String k : mSessionMap.keySet()) {
- if (DBG) log("addSipSession: " + k + ": " + mSessionMap.get(k));
- }
- }
- }
-
- private synchronized void removeSipSession(SipSessionImpl session) {
- if (session == mCallReceiverSession) return;
- String key = session.getCallId();
- SipSessionImpl s = mSessionMap.remove(key);
- // sanity check
- if ((s != null) && (s != session)) {
- if (DBG) log("removeSession: " + session + " is not associated with key '"
- + key + "'");
- mSessionMap.put(key, s);
- for (Map.Entry<String, SipSessionImpl> entry
- : mSessionMap.entrySet()) {
- if (entry.getValue() == s) {
- key = entry.getKey();
- mSessionMap.remove(key);
- }
- }
- }
-
- if ((s != null) && isLoggable(s)) {
- if (DBG) log("removeSession: " + session + " @key '" + key + "'");
- for (String k : mSessionMap.keySet()) {
- if (DBG) log("removeSession: " + k + ": " + mSessionMap.get(k));
- }
- }
- }
-
- @Override
- public void processRequest(final RequestEvent event) {
- if (isRequestEvent(Request.INVITE, event)) {
- if (DBG) log("processRequest: mWakeLock.acquire got INVITE, thread:"
- + Thread.currentThread());
- // Acquire a wake lock and keep it for WAKE_LOCK_HOLDING_TIME;
- // should be large enough to bring up the app.
- mWakeLock.acquire(WAKE_LOCK_HOLDING_TIME);
- }
- process(event);
- }
-
- @Override
- public void processResponse(ResponseEvent event) {
- process(event);
- }
-
- @Override
- public void processIOException(IOExceptionEvent event) {
- process(event);
- }
-
- @Override
- public void processTimeout(TimeoutEvent event) {
- process(event);
- }
-
- @Override
- public void processTransactionTerminated(TransactionTerminatedEvent event) {
- process(event);
- }
-
- @Override
- public void processDialogTerminated(DialogTerminatedEvent event) {
- process(event);
- }
-
- private synchronized void process(EventObject event) {
- SipSessionImpl session = getSipSession(event);
- try {
- boolean isLoggable = isLoggable(session, event);
- boolean processed = (session != null) && session.process(event);
- if (isLoggable && processed) {
- log("process: event new state after: "
- + SipSession.State.toString(session.mState));
- }
- } catch (Throwable e) {
- loge("process: error event=" + event, getRootCause(e));
- session.onError(e);
- }
- }
-
- private String extractContent(Message message) {
- // Currently we do not support secure MIME bodies.
- byte[] bytes = message.getRawContent();
- if (bytes != null) {
- try {
- if (message instanceof SIPMessage) {
- return ((SIPMessage) message).getMessageContent();
- } else {
- return new String(bytes, "UTF-8");
- }
- } catch (UnsupportedEncodingException e) {
- }
- }
- return null;
- }
-
- private void extractExternalAddress(ResponseEvent evt) {
- Response response = evt.getResponse();
- ViaHeader viaHeader = (ViaHeader)(response.getHeader(
- SIPHeaderNames.VIA));
- if (viaHeader == null) return;
- int rport = viaHeader.getRPort();
- String externalIp = viaHeader.getReceived();
- if ((rport > 0) && (externalIp != null)) {
- mExternalIp = externalIp;
- mExternalPort = rport;
- if (DBG) {
- log("extractExternalAddress: external addr " + externalIp + ":" + rport
- + " on " + mSipStack);
- }
- }
- }
-
- private Throwable getRootCause(Throwable exception) {
- Throwable cause = exception.getCause();
- while (cause != null) {
- exception = cause;
- cause = exception.getCause();
- }
- return exception;
- }
-
- private SipSessionImpl createNewSession(RequestEvent event,
- ISipSessionListener listener, ServerTransaction transaction,
- int newState) throws SipException {
- SipSessionImpl newSession = new SipSessionImpl(listener);
- newSession.mServerTransaction = transaction;
- newSession.mState = newState;
- newSession.mDialog = newSession.mServerTransaction.getDialog();
- newSession.mInviteReceived = event;
- newSession.mPeerProfile = createPeerProfile((HeaderAddress)
- event.getRequest().getHeader(FromHeader.NAME));
- newSession.mPeerSessionDescription =
- extractContent(event.getRequest());
- return newSession;
- }
-
- private class SipSessionCallReceiverImpl extends SipSessionImpl {
- private static final String SSCRI_TAG = "SipSessionCallReceiverImpl";
- private static final boolean SSCRI_DBG = true;
-
- public SipSessionCallReceiverImpl(ISipSessionListener listener) {
- super(listener);
- }
-
- private int processInviteWithReplaces(RequestEvent event,
- ReplacesHeader replaces) {
- String callId = replaces.getCallId();
- SipSessionImpl session = mSessionMap.get(callId);
- if (session == null) {
- return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST;
- }
-
- Dialog dialog = session.mDialog;
- if (dialog == null) return Response.DECLINE;
-
- if (!dialog.getLocalTag().equals(replaces.getToTag()) ||
- !dialog.getRemoteTag().equals(replaces.getFromTag())) {
- // No match is found, returns 481.
- return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST;
- }
-
- ReferredByHeader referredBy = (ReferredByHeader) event.getRequest()
- .getHeader(ReferredByHeader.NAME);
- if ((referredBy == null) ||
- !dialog.getRemoteParty().equals(referredBy.getAddress())) {
- return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST;
- }
- return Response.OK;
- }
-
- private void processNewInviteRequest(RequestEvent event)
- throws SipException {
- ReplacesHeader replaces = (ReplacesHeader) event.getRequest()
- .getHeader(ReplacesHeader.NAME);
- SipSessionImpl newSession = null;
- if (replaces != null) {
- int response = processInviteWithReplaces(event, replaces);
- if (SSCRI_DBG) {
- log("processNewInviteRequest: " + replaces
- + " response=" + response);
- }
- if (response == Response.OK) {
- SipSessionImpl replacedSession =
- mSessionMap.get(replaces.getCallId());
- // got INVITE w/ replaces request.
- newSession = createNewSession(event,
- replacedSession.mProxy.getListener(),
- mSipHelper.getServerTransaction(event),
- SipSession.State.INCOMING_CALL);
- newSession.mProxy.onCallTransferring(newSession,
- newSession.mPeerSessionDescription);
- } else {
- mSipHelper.sendResponse(event, response);
- }
- } else {
- // New Incoming call.
- newSession = createNewSession(event, mProxy,
- mSipHelper.sendRinging(event, generateTag()),
- SipSession.State.INCOMING_CALL);
- mProxy.onRinging(newSession, newSession.mPeerProfile,
- newSession.mPeerSessionDescription);
- }
- if (newSession != null) addSipSession(newSession);
- }
-
- @Override
- public boolean process(EventObject evt) throws SipException {
- if (isLoggable(this, evt)) log("process: " + this + ": "
- + SipSession.State.toString(mState) + ": processing "
- + logEvt(evt));
- if (isRequestEvent(Request.INVITE, evt)) {
- processNewInviteRequest((RequestEvent) evt);
- return true;
- } else if (isRequestEvent(Request.OPTIONS, evt)) {
- mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
- return true;
- } else {
- return false;
- }
- }
-
- private void log(String s) {
- Rlog.d(SSCRI_TAG, s);
- }
- }
-
- static interface KeepAliveProcessCallback {
- /** Invoked when the response of keeping alive comes back. */
- void onResponse(boolean portChanged);
- void onError(int errorCode, String description);
- }
-
- class SipSessionImpl extends ISipSession.Stub {
- private static final String SSI_TAG = "SipSessionImpl";
- private static final boolean SSI_DBG = true;
-
- SipProfile mPeerProfile;
- SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
- int mState = SipSession.State.READY_TO_CALL;
- RequestEvent mInviteReceived;
- Dialog mDialog;
- ServerTransaction mServerTransaction;
- ClientTransaction mClientTransaction;
- String mPeerSessionDescription;
- boolean mInCall;
- SessionTimer mSessionTimer;
- int mAuthenticationRetryCount;
-
- private SipKeepAlive mSipKeepAlive;
-
- private SipSessionImpl mSipSessionImpl;
-
- // the following three members are used for handling refer request.
- SipSessionImpl mReferSession;
- ReferredByHeader mReferredBy;
- String mReplaces;
-
- // lightweight timer
- class SessionTimer {
- private boolean mRunning = true;
-
- void start(final int timeout) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- sleep(timeout);
- if (mRunning) timeout();
- }
- }, "SipSessionTimerThread").start();
- }
-
- synchronized void cancel() {
- mRunning = false;
- this.notify();
- }
-
- private void timeout() {
- synchronized (SipSessionGroup.this) {
- onError(SipErrorCode.TIME_OUT, "Session timed out!");
- }
- }
-
- private synchronized void sleep(int timeout) {
- try {
- this.wait(timeout * 1000);
- } catch (InterruptedException e) {
- loge("session timer interrupted!", e);
- }
- }
- }
-
- public SipSessionImpl(ISipSessionListener listener) {
- setListener(listener);
- }
-
- SipSessionImpl duplicate() {
- return new SipSessionImpl(mProxy.getListener());
- }
-
- private void reset() {
- mInCall = false;
- removeSipSession(this);
- mPeerProfile = null;
- mState = SipSession.State.READY_TO_CALL;
- mInviteReceived = null;
- mPeerSessionDescription = null;
- mAuthenticationRetryCount = 0;
- mReferSession = null;
- mReferredBy = null;
- mReplaces = null;
-
- if (mDialog != null) mDialog.delete();
- mDialog = null;
-
- try {
- if (mServerTransaction != null) mServerTransaction.terminate();
- } catch (ObjectInUseException e) {
- // ignored
- }
- mServerTransaction = null;
-
- try {
- if (mClientTransaction != null) mClientTransaction.terminate();
- } catch (ObjectInUseException e) {
- // ignored
- }
- mClientTransaction = null;
-
- cancelSessionTimer();
-
- if (mSipSessionImpl != null) {
- mSipSessionImpl.stopKeepAliveProcess();
- mSipSessionImpl = null;
- }
- }
-
- @Override
- public boolean isInCall() {
- return mInCall;
- }
-
- @Override
- public String getLocalIp() {
- return mLocalIp;
- }
-
- @Override
- public SipProfile getLocalProfile() {
- return mLocalProfile;
- }
-
- @Override
- public SipProfile getPeerProfile() {
- return mPeerProfile;
- }
-
- @Override
- public String getCallId() {
- return SipHelper.getCallId(getTransaction());
- }
-
- private Transaction getTransaction() {
- if (mClientTransaction != null) return mClientTransaction;
- if (mServerTransaction != null) return mServerTransaction;
- return null;
- }
-
- @Override
- public int getState() {
- return mState;
- }
-
- @Override
- public void setListener(ISipSessionListener listener) {
- mProxy.setListener((listener instanceof SipSessionListenerProxy)
- ? ((SipSessionListenerProxy) listener).getListener()
- : listener);
- }
-
- // process the command in a new thread
- private void doCommandAsync(final EventObject command) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- processCommand(command);
- } catch (Throwable e) {
- loge("command error: " + command + ": "
- + mLocalProfile.getUriString(),
- getRootCause(e));
- onError(e);
- }
- }
- }, "SipSessionAsyncCmdThread").start();
- }
-
- @Override
- public void makeCall(SipProfile peerProfile, String sessionDescription,
- int timeout) {
- doCommandAsync(new MakeCallCommand(peerProfile, sessionDescription,
- timeout));
- }
-
- @Override
- public void answerCall(String sessionDescription, int timeout) {
- synchronized (SipSessionGroup.this) {
- if (mPeerProfile == null) return;
- doCommandAsync(new MakeCallCommand(mPeerProfile,
- sessionDescription, timeout));
- }
- }
-
- @Override
- public void endCall() {
- doCommandAsync(END_CALL);
- }
-
- @Override
- public void changeCall(String sessionDescription, int timeout) {
- synchronized (SipSessionGroup.this) {
- if (mPeerProfile == null) return;
- doCommandAsync(new MakeCallCommand(mPeerProfile,
- sessionDescription, timeout));
- }
- }
-
- @Override
- public void register(int duration) {
- doCommandAsync(new RegisterCommand(duration));
- }
-
- @Override
- public void unregister() {
- doCommandAsync(DEREGISTER);
- }
-
- private void processCommand(EventObject command) throws SipException {
- if (isLoggable(command)) log("process cmd: " + command);
- if (!process(command)) {
- onError(SipErrorCode.IN_PROGRESS,
- "cannot initiate a new transaction to execute: "
- + command);
- }
- }
-
- protected String generateTag() {
- // 32-bit randomness
- return String.valueOf((long) (Math.random() * 0x100000000L));
- }
-
- @Override
- public String toString() {
- try {
- String s = super.toString();
- return s.substring(s.indexOf("@")) + ":"
- + SipSession.State.toString(mState);
- } catch (Throwable e) {
- return super.toString();
- }
- }
-
- public boolean process(EventObject evt) throws SipException {
- if (isLoggable(this, evt)) log(" ~~~~~ " + this + ": "
- + SipSession.State.toString(mState) + ": processing "
- + logEvt(evt));
- synchronized (SipSessionGroup.this) {
- if (isClosed()) return false;
-
- if (mSipKeepAlive != null) {
- // event consumed by keepalive process
- if (mSipKeepAlive.process(evt)) return true;
- }
-
- Dialog dialog = null;
- if (evt instanceof RequestEvent) {
- dialog = ((RequestEvent) evt).getDialog();
- } else if (evt instanceof ResponseEvent) {
- dialog = ((ResponseEvent) evt).getDialog();
- extractExternalAddress((ResponseEvent) evt);
- }
- if (dialog != null) mDialog = dialog;
-
- boolean processed;
-
- switch (mState) {
- case SipSession.State.REGISTERING:
- case SipSession.State.DEREGISTERING:
- processed = registeringToReady(evt);
- break;
- case SipSession.State.READY_TO_CALL:
- processed = readyForCall(evt);
- break;
- case SipSession.State.INCOMING_CALL:
- processed = incomingCall(evt);
- break;
- case SipSession.State.INCOMING_CALL_ANSWERING:
- processed = incomingCallToInCall(evt);
- break;
- case SipSession.State.OUTGOING_CALL:
- case SipSession.State.OUTGOING_CALL_RING_BACK:
- processed = outgoingCall(evt);
- break;
- case SipSession.State.OUTGOING_CALL_CANCELING:
- processed = outgoingCallToReady(evt);
- break;
- case SipSession.State.IN_CALL:
- processed = inCall(evt);
- break;
- case SipSession.State.ENDING_CALL:
- processed = endingCall(evt);
- break;
- default:
- processed = false;
- }
- return (processed || processExceptions(evt));
- }
- }
-
- private boolean processExceptions(EventObject evt) throws SipException {
- if (isRequestEvent(Request.BYE, evt)) {
- // terminate the call whenever a BYE is received
- mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
- endCallNormally();
- return true;
- } else if (isRequestEvent(Request.CANCEL, evt)) {
- mSipHelper.sendResponse((RequestEvent) evt,
- Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST);
- return true;
- } else if (evt instanceof TransactionTerminatedEvent) {
- if (isCurrentTransaction((TransactionTerminatedEvent) evt)) {
- if (evt instanceof TimeoutEvent) {
- processTimeout((TimeoutEvent) evt);
- } else {
- processTransactionTerminated(
- (TransactionTerminatedEvent) evt);
- }
- return true;
- }
- } else if (isRequestEvent(Request.OPTIONS, evt)) {
- mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
- return true;
- } else if (evt instanceof DialogTerminatedEvent) {
- processDialogTerminated((DialogTerminatedEvent) evt);
- return true;
- }
- return false;
- }
-
- private void processDialogTerminated(DialogTerminatedEvent event) {
- if (mDialog == event.getDialog()) {
- onError(new SipException("dialog terminated"));
- } else {
- if (SSI_DBG) log("not the current dialog; current=" + mDialog
- + ", terminated=" + event.getDialog());
- }
- }
-
- private boolean isCurrentTransaction(TransactionTerminatedEvent event) {
- Transaction current = event.isServerTransaction()
- ? mServerTransaction
- : mClientTransaction;
- Transaction target = event.isServerTransaction()
- ? event.getServerTransaction()
- : event.getClientTransaction();
-
- if ((current != target) && (mState != SipSession.State.PINGING)) {
- if (SSI_DBG) log("not the current transaction; current="
- + toString(current) + ", target=" + toString(target));
- return false;
- } else if (current != null) {
- if (SSI_DBG) log("transaction terminated: " + toString(current));
- return true;
- } else {
- // no transaction; shouldn't be here; ignored
- return true;
- }
- }
-
- private String toString(Transaction transaction) {
- if (transaction == null) return "null";
- Request request = transaction.getRequest();
- Dialog dialog = transaction.getDialog();
- CSeqHeader cseq = (CSeqHeader) request.getHeader(CSeqHeader.NAME);
- return String.format("req=%s,%s,s=%s,ds=%s,", request.getMethod(),
- cseq.getSeqNumber(), transaction.getState(),
- ((dialog == null) ? "-" : dialog.getState()));
- }
-
- private void processTransactionTerminated(
- TransactionTerminatedEvent event) {
- switch (mState) {
- case SipSession.State.IN_CALL:
- case SipSession.State.READY_TO_CALL:
- if (SSI_DBG) log("Transaction terminated; do nothing");
- break;
- default:
- if (SSI_DBG) log("Transaction terminated early: " + this);
- onError(SipErrorCode.TRANSACTION_TERMINTED,
- "transaction terminated");
- }
- }
-
- private void processTimeout(TimeoutEvent event) {
- if (SSI_DBG) log("processing Timeout...");
- switch (mState) {
- case SipSession.State.REGISTERING:
- case SipSession.State.DEREGISTERING:
- reset();
- mProxy.onRegistrationTimeout(this);
- break;
- 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;
-
- default:
- if (SSI_DBG) log(" do nothing");
- break;
- }
- }
-
- private int getExpiryTime(Response response) {
- int time = -1;
- ContactHeader contact = (ContactHeader) response.getHeader(ContactHeader.NAME);
- if (contact != null) {
- time = contact.getExpires();
- }
- ExpiresHeader expires = (ExpiresHeader) response.getHeader(ExpiresHeader.NAME);
- if (expires != null && (time < 0 || time > expires.getExpires())) {
- time = expires.getExpires();
- }
- if (time <= 0) {
- time = EXPIRY_TIME;
- }
- expires = (ExpiresHeader) response.getHeader(MinExpiresHeader.NAME);
- if (expires != null && time < expires.getExpires()) {
- time = expires.getExpires();
- }
- if (SSI_DBG) {
- log("Expiry time = " + time);
- }
- return time;
- }
-
- private boolean registeringToReady(EventObject evt)
- throws SipException {
- if (expectResponse(Request.REGISTER, evt)) {
- ResponseEvent event = (ResponseEvent) evt;
- Response response = event.getResponse();
-
- int statusCode = response.getStatusCode();
- switch (statusCode) {
- case Response.OK:
- int state = mState;
- onRegistrationDone((state == SipSession.State.REGISTERING)
- ? getExpiryTime(((ResponseEvent) evt).getResponse())
- : -1);
- return true;
- case Response.UNAUTHORIZED:
- case Response.PROXY_AUTHENTICATION_REQUIRED:
- handleAuthentication(event);
- return true;
- default:
- if (statusCode >= 500) {
- onRegistrationFailed(response);
- return true;
- }
- }
- }
- return false;
- }
-
- private boolean handleAuthentication(ResponseEvent event)
- throws SipException {
- Response response = event.getResponse();
- String nonce = getNonceFromResponse(response);
- if (nonce == null) {
- onError(SipErrorCode.SERVER_ERROR,
- "server does not provide challenge");
- return false;
- } else if (mAuthenticationRetryCount < 2) {
- mClientTransaction = mSipHelper.handleChallenge(
- event, getAccountManager());
- mDialog = mClientTransaction.getDialog();
- mAuthenticationRetryCount++;
- if (isLoggable(this, event)) {
- if (SSI_DBG) log(" authentication retry count="
- + mAuthenticationRetryCount);
- }
- return true;
- } else {
- if (crossDomainAuthenticationRequired(response)) {
- onError(SipErrorCode.CROSS_DOMAIN_AUTHENTICATION,
- getRealmFromResponse(response));
- } else {
- onError(SipErrorCode.INVALID_CREDENTIALS,
- "incorrect username or password");
- }
- return false;
- }
- }
-
- private boolean crossDomainAuthenticationRequired(Response response) {
- String realm = getRealmFromResponse(response);
- if (realm == null) realm = "";
- return !mLocalProfile.getSipDomain().trim().equals(realm.trim());
- }
-
- private AccountManager getAccountManager() {
- return new AccountManager() {
- @Override
- public UserCredentials getCredentials(ClientTransaction
- challengedTransaction, String realm) {
- return new UserCredentials() {
- @Override
- public String getUserName() {
- String username = mLocalProfile.getAuthUserName();
- return (!TextUtils.isEmpty(username) ? username :
- mLocalProfile.getUserName());
- }
-
- @Override
- public String getPassword() {
- return mPassword;
- }
-
- @Override
- public String getSipDomain() {
- return mLocalProfile.getSipDomain();
- }
- };
- }
- };
- }
-
- private String getRealmFromResponse(Response response) {
- WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader(
- SIPHeaderNames.WWW_AUTHENTICATE);
- if (wwwAuth != null) return wwwAuth.getRealm();
- ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader(
- SIPHeaderNames.PROXY_AUTHENTICATE);
- return (proxyAuth == null) ? null : proxyAuth.getRealm();
- }
-
- private String getNonceFromResponse(Response response) {
- WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader(
- SIPHeaderNames.WWW_AUTHENTICATE);
- if (wwwAuth != null) return wwwAuth.getNonce();
- ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader(
- SIPHeaderNames.PROXY_AUTHENTICATE);
- return (proxyAuth == null) ? null : proxyAuth.getNonce();
- }
-
- private String getResponseString(int statusCode) {
- StatusLine statusLine = new StatusLine();
- statusLine.setStatusCode(statusCode);
- statusLine.setReasonPhrase(SIPResponse.getReasonPhrase(statusCode));
- return statusLine.encode();
- }
-
- private boolean readyForCall(EventObject evt) throws SipException {
- // expect MakeCallCommand, RegisterCommand, DEREGISTER
- if (evt instanceof MakeCallCommand) {
- mState = SipSession.State.OUTGOING_CALL;
- MakeCallCommand cmd = (MakeCallCommand) evt;
- mPeerProfile = cmd.getPeerProfile();
- if (mReferSession != null) {
- mSipHelper.sendReferNotify(mReferSession.mDialog,
- getResponseString(Response.TRYING));
- }
- mClientTransaction = mSipHelper.sendInvite(
- mLocalProfile, mPeerProfile, cmd.getSessionDescription(),
- generateTag(), mReferredBy, mReplaces);
- mDialog = mClientTransaction.getDialog();
- addSipSession(this);
- startSessionTimer(cmd.getTimeout());
- mProxy.onCalling(this);
- return true;
- } else if (evt instanceof RegisterCommand) {
- mState = SipSession.State.REGISTERING;
- int duration = ((RegisterCommand) evt).getDuration();
- mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
- generateTag(), duration);
- mDialog = mClientTransaction.getDialog();
- addSipSession(this);
- mProxy.onRegistering(this);
- return true;
- } else if (DEREGISTER == evt) {
- mState = SipSession.State.DEREGISTERING;
- mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
- generateTag(), 0);
- mDialog = mClientTransaction.getDialog();
- addSipSession(this);
- mProxy.onRegistering(this);
- return true;
- }
- return false;
- }
-
- private boolean incomingCall(EventObject evt) throws SipException {
- // expect MakeCallCommand(answering) , END_CALL cmd , Cancel
- if (evt instanceof MakeCallCommand) {
- // answer call
- mState = SipSession.State.INCOMING_CALL_ANSWERING;
- mServerTransaction = mSipHelper.sendInviteOk(mInviteReceived,
- mLocalProfile,
- ((MakeCallCommand) evt).getSessionDescription(),
- mServerTransaction,
- mExternalIp, mExternalPort);
- startSessionTimer(((MakeCallCommand) evt).getTimeout());
- return true;
- } else if (END_CALL == evt) {
- mSipHelper.sendInviteBusyHere(mInviteReceived,
- mServerTransaction);
- endCallNormally();
- return true;
- } else if (isRequestEvent(Request.CANCEL, evt)) {
- RequestEvent event = (RequestEvent) evt;
- mSipHelper.sendResponse(event, Response.OK);
- mSipHelper.sendInviteRequestTerminated(
- mInviteReceived.getRequest(), mServerTransaction);
- endCallNormally();
- return true;
- }
- return false;
- }
-
- private boolean incomingCallToInCall(EventObject evt) {
- // expect ACK, CANCEL request
- if (isRequestEvent(Request.ACK, evt)) {
- String sdp = extractContent(((RequestEvent) evt).getRequest());
- if (sdp != null) mPeerSessionDescription = sdp;
- if (mPeerSessionDescription == null) {
- onError(SipErrorCode.CLIENT_ERROR, "peer sdp is empty");
- } else {
- establishCall(false);
- }
- return true;
- } else if (isRequestEvent(Request.CANCEL, evt)) {
- // http://tools.ietf.org/html/rfc3261#section-9.2
- // Final response has been sent; do nothing here.
- return true;
- }
- return false;
- }
-
- private boolean outgoingCall(EventObject evt) throws SipException {
- if (expectResponse(Request.INVITE, evt)) {
- ResponseEvent event = (ResponseEvent) evt;
- Response response = event.getResponse();
-
- int statusCode = response.getStatusCode();
- switch (statusCode) {
- case Response.RINGING:
- case Response.CALL_IS_BEING_FORWARDED:
- case Response.QUEUED:
- case Response.SESSION_PROGRESS:
- // feedback any provisional responses (except TRYING) as
- // ring back for better UX
- if (mState == SipSession.State.OUTGOING_CALL) {
- mState = SipSession.State.OUTGOING_CALL_RING_BACK;
- cancelSessionTimer();
- mProxy.onRingingBack(this);
- }
- return true;
- case Response.OK:
- if (mReferSession != null) {
- mSipHelper.sendReferNotify(mReferSession.mDialog,
- getResponseString(Response.OK));
- // since we don't need to remember the session anymore.
- mReferSession = null;
- }
- mSipHelper.sendInviteAck(event, mDialog);
- mPeerSessionDescription = extractContent(response);
- establishCall(true);
- return true;
- case Response.UNAUTHORIZED:
- case Response.PROXY_AUTHENTICATION_REQUIRED:
- if (handleAuthentication(event)) {
- addSipSession(this);
- }
- return true;
- case Response.REQUEST_PENDING:
- // TODO: rfc3261#section-14.1; re-schedule invite
- return true;
- default:
- if (mReferSession != null) {
- mSipHelper.sendReferNotify(mReferSession.mDialog,
- getResponseString(Response.SERVICE_UNAVAILABLE));
- }
- if (statusCode >= 400) {
- // error: an ack is sent automatically by the stack
- onError(response);
- return true;
- } else if (statusCode >= 300) {
- // TODO: handle 3xx (redirect)
- } else {
- return true;
- }
- }
- return false;
- } else if (END_CALL == evt) {
- // RFC says that UA should not send out cancel when no
- // response comes back yet. We are cheating for not checking
- // response.
- mState = SipSession.State.OUTGOING_CALL_CANCELING;
- mSipHelper.sendCancel(mClientTransaction);
- startSessionTimer(CANCEL_CALL_TIMER);
- return true;
- } else if (isRequestEvent(Request.INVITE, evt)) {
- // Call self? Send BUSY HERE so server may redirect the call to
- // voice mailbox.
- RequestEvent event = (RequestEvent) evt;
- mSipHelper.sendInviteBusyHere(event,
- event.getServerTransaction());
- return true;
- }
- return false;
- }
-
- private boolean outgoingCallToReady(EventObject evt)
- throws SipException {
- if (evt instanceof ResponseEvent) {
- ResponseEvent event = (ResponseEvent) evt;
- Response response = event.getResponse();
- int statusCode = response.getStatusCode();
- if (expectResponse(Request.CANCEL, evt)) {
- if (statusCode == Response.OK) {
- // do nothing; wait for REQUEST_TERMINATED
- return true;
- }
- } else if (expectResponse(Request.INVITE, evt)) {
- switch (statusCode) {
- case Response.OK:
- outgoingCall(evt); // abort Cancel
- return true;
- case Response.REQUEST_TERMINATED:
- endCallNormally();
- return true;
- }
- } else {
- return false;
- }
-
- if (statusCode >= 400) {
- onError(response);
- return true;
- }
- } else if (evt instanceof TransactionTerminatedEvent) {
- // rfc3261#section-14.1:
- // if re-invite gets timed out, terminate the dialog; but
- // re-invite is not reliable, just let it go and pretend
- // nothing happened.
- onError(new SipException("timed out"));
- }
- return false;
- }
-
- private boolean processReferRequest(RequestEvent event)
- throws SipException {
- try {
- ReferToHeader referto = (ReferToHeader) event.getRequest()
- .getHeader(ReferTo.NAME);
- Address address = referto.getAddress();
- SipURI uri = (SipURI) address.getURI();
- String replacesHeader = uri.getHeader(ReplacesHeader.NAME);
- String username = uri.getUser();
- if (username == null) {
- mSipHelper.sendResponse(event, Response.BAD_REQUEST);
- return false;
- }
- // send notify accepted
- mSipHelper.sendResponse(event, Response.ACCEPTED);
- SipSessionImpl newSession = createNewSession(event,
- this.mProxy.getListener(),
- mSipHelper.getServerTransaction(event),
- SipSession.State.READY_TO_CALL);
- newSession.mReferSession = this;
- newSession.mReferredBy = (ReferredByHeader) event.getRequest()
- .getHeader(ReferredByHeader.NAME);
- newSession.mReplaces = replacesHeader;
- newSession.mPeerProfile = createPeerProfile(referto);
- newSession.mProxy.onCallTransferring(newSession,
- null);
- return true;
- } catch (IllegalArgumentException e) {
- throw new SipException("createPeerProfile()", e);
- }
- }
-
- private boolean inCall(EventObject evt) throws SipException {
- // expect END_CALL cmd, BYE request, hold call (MakeCallCommand)
- // OK retransmission is handled in SipStack
- if (END_CALL == evt) {
- // rfc3261#section-15.1.1
- mState = SipSession.State.ENDING_CALL;
- mSipHelper.sendBye(mDialog);
- mProxy.onCallEnded(this);
- startSessionTimer(END_CALL_TIMER);
- return true;
- } else if (isRequestEvent(Request.INVITE, evt)) {
- // got Re-INVITE
- mState = SipSession.State.INCOMING_CALL;
- RequestEvent event = mInviteReceived = (RequestEvent) evt;
- mPeerSessionDescription = extractContent(event.getRequest());
- mServerTransaction = null;
- mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription);
- return true;
- } else if (isRequestEvent(Request.BYE, evt)) {
- mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
- endCallNormally();
- return true;
- } else if (isRequestEvent(Request.REFER, evt)) {
- return processReferRequest((RequestEvent) evt);
- } else if (evt instanceof MakeCallCommand) {
- // to change call
- mState = SipSession.State.OUTGOING_CALL;
- mClientTransaction = mSipHelper.sendReinvite(mDialog,
- ((MakeCallCommand) evt).getSessionDescription());
- startSessionTimer(((MakeCallCommand) evt).getTimeout());
- return true;
- } else if (evt instanceof ResponseEvent) {
- if (expectResponse(Request.NOTIFY, evt)) return true;
- }
- return false;
- }
-
- private boolean endingCall(EventObject evt) throws SipException {
- if (expectResponse(Request.BYE, evt)) {
- ResponseEvent event = (ResponseEvent) evt;
- Response response = event.getResponse();
-
- int statusCode = response.getStatusCode();
- switch (statusCode) {
- case Response.UNAUTHORIZED:
- case Response.PROXY_AUTHENTICATION_REQUIRED:
- if (handleAuthentication(event)) {
- return true;
- } else {
- // can't authenticate; pass through to end session
- }
- }
- cancelSessionTimer();
- reset();
- return true;
- }
- return false;
- }
-
- // timeout in seconds
- private void startSessionTimer(int timeout) {
- if (timeout > 0) {
- mSessionTimer = new SessionTimer();
- mSessionTimer.start(timeout);
- }
- }
-
- private void cancelSessionTimer() {
- if (mSessionTimer != null) {
- mSessionTimer.cancel();
- mSessionTimer = null;
- }
- }
-
- private String createErrorMessage(Response response) {
- return String.format("%s (%d)", response.getReasonPhrase(),
- response.getStatusCode());
- }
-
- private void enableKeepAlive() {
- if (mSipSessionImpl != null) {
- mSipSessionImpl.stopKeepAliveProcess();
- } else {
- mSipSessionImpl = duplicate();
- }
- try {
- mSipSessionImpl.startKeepAliveProcess(
- INCALL_KEEPALIVE_INTERVAL, mPeerProfile, null);
- } catch (SipException e) {
- loge("keepalive cannot be enabled; ignored", e);
- mSipSessionImpl.stopKeepAliveProcess();
- }
- }
-
- private void establishCall(boolean enableKeepAlive) {
- mState = SipSession.State.IN_CALL;
- cancelSessionTimer();
- if (!mInCall && enableKeepAlive) enableKeepAlive();
- mInCall = true;
- mProxy.onCallEstablished(this, mPeerSessionDescription);
- }
-
- private void endCallNormally() {
- reset();
- mProxy.onCallEnded(this);
- }
-
- private void endCallOnError(int errorCode, String message) {
- reset();
- mProxy.onError(this, errorCode, message);
- }
-
- private void endCallOnBusy() {
- reset();
- mProxy.onCallBusy(this);
- }
-
- private void onError(int errorCode, String message) {
- cancelSessionTimer();
- switch (mState) {
- case SipSession.State.REGISTERING:
- case SipSession.State.DEREGISTERING:
- onRegistrationFailed(errorCode, message);
- break;
- default:
- endCallOnError(errorCode, message);
- }
- }
-
-
- private void onError(Throwable exception) {
- exception = getRootCause(exception);
- onError(getErrorCode(exception), exception.toString());
- }
-
- private void onError(Response response) {
- int statusCode = response.getStatusCode();
- if (!mInCall && (statusCode == Response.BUSY_HERE)) {
- endCallOnBusy();
- } else {
- onError(getErrorCode(statusCode), createErrorMessage(response));
- }
- }
-
- private int getErrorCode(int responseStatusCode) {
- switch (responseStatusCode) {
- case Response.TEMPORARILY_UNAVAILABLE:
- case Response.FORBIDDEN:
- case Response.GONE:
- case Response.NOT_FOUND:
- case Response.NOT_ACCEPTABLE:
- case Response.NOT_ACCEPTABLE_HERE:
- return SipErrorCode.PEER_NOT_REACHABLE;
-
- case Response.REQUEST_URI_TOO_LONG:
- case Response.ADDRESS_INCOMPLETE:
- case Response.AMBIGUOUS:
- return SipErrorCode.INVALID_REMOTE_URI;
-
- case Response.REQUEST_TIMEOUT:
- return SipErrorCode.TIME_OUT;
-
- default:
- if (responseStatusCode < 500) {
- return SipErrorCode.CLIENT_ERROR;
- } else {
- return SipErrorCode.SERVER_ERROR;
- }
- }
- }
-
- private int getErrorCode(Throwable exception) {
- String message = exception.getMessage();
- if (exception instanceof UnknownHostException) {
- return SipErrorCode.SERVER_UNREACHABLE;
- } else if (exception instanceof IOException) {
- return SipErrorCode.SOCKET_ERROR;
- } else {
- return SipErrorCode.CLIENT_ERROR;
- }
- }
-
- private void onRegistrationDone(int duration) {
- reset();
- mProxy.onRegistrationDone(this, duration);
- }
-
- private void onRegistrationFailed(int errorCode, String message) {
- reset();
- mProxy.onRegistrationFailed(this, errorCode, message);
- }
-
- private void onRegistrationFailed(Response response) {
- int statusCode = response.getStatusCode();
- onRegistrationFailed(getErrorCode(statusCode),
- createErrorMessage(response));
- }
-
- // Notes: SipSessionListener will be replaced by the keepalive process
- // @param interval in seconds
- public void startKeepAliveProcess(int interval,
- KeepAliveProcessCallback callback) throws SipException {
- synchronized (SipSessionGroup.this) {
- startKeepAliveProcess(interval, mLocalProfile, callback);
- }
- }
-
- // Notes: SipSessionListener will be replaced by the keepalive process
- // @param interval in seconds
- public void startKeepAliveProcess(int interval, SipProfile peerProfile,
- KeepAliveProcessCallback callback) throws SipException {
- synchronized (SipSessionGroup.this) {
- if (mSipKeepAlive != null) {
- throw new SipException("Cannot create more than one "
- + "keepalive process in a SipSession");
- }
- mPeerProfile = peerProfile;
- mSipKeepAlive = new SipKeepAlive();
- mProxy.setListener(mSipKeepAlive);
- mSipKeepAlive.start(interval, callback);
- }
- }
-
- public void stopKeepAliveProcess() {
- synchronized (SipSessionGroup.this) {
- if (mSipKeepAlive != null) {
- mSipKeepAlive.stop();
- mSipKeepAlive = null;
- }
- }
- }
-
- class SipKeepAlive extends SipSessionAdapter implements Runnable {
- private static final String SKA_TAG = "SipKeepAlive";
- private static final boolean SKA_DBG = true;
-
- private boolean mRunning = false;
- private KeepAliveProcessCallback mCallback;
-
- private boolean mPortChanged = false;
- private int mRPort = 0;
- private int mInterval; // just for debugging
-
- // @param interval in seconds
- void start(int interval, KeepAliveProcessCallback callback) {
- if (mRunning) return;
- mRunning = true;
- mInterval = interval;
- mCallback = new KeepAliveProcessCallbackProxy(callback);
- mWakeupTimer.set(interval * 1000, this);
- if (SKA_DBG) {
- log("start keepalive:"
- + mLocalProfile.getUriString());
- }
-
- // No need to run the first time in a separate thread for now
- run();
- }
-
- // return true if the event is consumed
- boolean process(EventObject evt) {
- if (mRunning && (mState == SipSession.State.PINGING)) {
- if (evt instanceof ResponseEvent) {
- if (parseOptionsResult(evt)) {
- if (mPortChanged) {
- resetExternalAddress();
- stop();
- } else {
- cancelSessionTimer();
- removeSipSession(SipSessionImpl.this);
- }
- mCallback.onResponse(mPortChanged);
- return true;
- }
- }
- }
- return false;
- }
-
- // SipSessionAdapter
- // To react to the session timeout event and network error.
- @Override
- public void onError(ISipSession session, int errorCode, String message) {
- stop();
- mCallback.onError(errorCode, message);
- }
-
- // SipWakeupTimer timeout handler
- // To send out keepalive message.
- @Override
- public void run() {
- synchronized (SipSessionGroup.this) {
- if (!mRunning) return;
-
- if (DBG_PING) {
- String peerUri = (mPeerProfile == null)
- ? "null"
- : mPeerProfile.getUriString();
- log("keepalive: " + mLocalProfile.getUriString()
- + " --> " + peerUri + ", interval=" + mInterval);
- }
- try {
- sendKeepAlive();
- } catch (Throwable t) {
- if (SKA_DBG) {
- loge("keepalive error: "
- + mLocalProfile.getUriString(), getRootCause(t));
- }
- // It's possible that the keepalive process is being stopped
- // during session.sendKeepAlive() so need to check mRunning
- // again here.
- if (mRunning) SipSessionImpl.this.onError(t);
- }
- }
- }
-
- void stop() {
- synchronized (SipSessionGroup.this) {
- if (SKA_DBG) {
- log("stop keepalive:" + mLocalProfile.getUriString()
- + ",RPort=" + mRPort);
- }
- mRunning = false;
- mWakeupTimer.cancel(this);
- reset();
- }
- }
-
- private void sendKeepAlive() throws SipException {
- synchronized (SipSessionGroup.this) {
- mState = SipSession.State.PINGING;
- mClientTransaction = mSipHelper.sendOptions(
- mLocalProfile, mPeerProfile, generateTag());
- mDialog = mClientTransaction.getDialog();
- addSipSession(SipSessionImpl.this);
-
- startSessionTimer(KEEPALIVE_TIMEOUT);
- // when timed out, onError() will be called with SipErrorCode.TIME_OUT
- }
- }
-
- private boolean parseOptionsResult(EventObject evt) {
- if (expectResponse(Request.OPTIONS, evt)) {
- ResponseEvent event = (ResponseEvent) evt;
- int rPort = getRPortFromResponse(event.getResponse());
- if (rPort != -1) {
- if (mRPort == 0) mRPort = rPort;
- if (mRPort != rPort) {
- mPortChanged = true;
- if (SKA_DBG) log(String.format(
- "rport is changed: %d <> %d", mRPort, rPort));
- mRPort = rPort;
- } else {
- if (SKA_DBG) log("rport is the same: " + rPort);
- }
- } else {
- if (SKA_DBG) log("peer did not respond rport");
- }
- return true;
- }
- return false;
- }
-
- private int getRPortFromResponse(Response response) {
- ViaHeader viaHeader = (ViaHeader)(response.getHeader(
- SIPHeaderNames.VIA));
- return (viaHeader == null) ? -1 : viaHeader.getRPort();
- }
-
- private void log(String s) {
- Rlog.d(SKA_TAG, s);
- }
- }
-
- private void log(String s) {
- Rlog.d(SSI_TAG, s);
- }
- }
-
- /**
- * @return true if the event is a request event matching the specified
- * method; false otherwise
- */
- private static boolean isRequestEvent(String method, EventObject event) {
- try {
- if (event instanceof RequestEvent) {
- RequestEvent requestEvent = (RequestEvent) event;
- return method.equals(requestEvent.getRequest().getMethod());
- }
- } catch (Throwable e) {
- }
- return false;
- }
-
- private static String getCseqMethod(Message message) {
- return ((CSeqHeader) message.getHeader(CSeqHeader.NAME)).getMethod();
- }
-
- /**
- * @return true if the event is a response event and the CSeqHeader method
- * match the given arguments; false otherwise
- */
- private static boolean expectResponse(
- String expectedMethod, EventObject evt) {
- if (evt instanceof ResponseEvent) {
- ResponseEvent event = (ResponseEvent) evt;
- Response response = event.getResponse();
- return expectedMethod.equalsIgnoreCase(getCseqMethod(response));
- }
- return false;
- }
-
- private static SipProfile createPeerProfile(HeaderAddress header)
- throws SipException {
- try {
- Address address = header.getAddress();
- SipURI uri = (SipURI) address.getURI();
- String username = uri.getUser();
- if (username == null) username = ANONYMOUS;
- int port = uri.getPort();
- SipProfile.Builder builder =
- new SipProfile.Builder(username, uri.getHost())
- .setDisplayName(address.getDisplayName());
- if (port > 0) builder.setPort(port);
- return builder.build();
- } catch (IllegalArgumentException e) {
- throw new SipException("createPeerProfile()", e);
- } catch (ParseException e) {
- throw new SipException("createPeerProfile()", e);
- }
- }
-
- private static boolean isLoggable(SipSessionImpl s) {
- if (s != null) {
- switch (s.mState) {
- case SipSession.State.PINGING:
- return DBG_PING;
- }
- }
- return DBG;
- }
-
- private static boolean isLoggable(EventObject evt) {
- return isLoggable(null, evt);
- }
-
- private static boolean isLoggable(SipSessionImpl s, EventObject evt) {
- if (!isLoggable(s)) return false;
- if (evt == null) return false;
-
- if (evt instanceof ResponseEvent) {
- Response response = ((ResponseEvent) evt).getResponse();
- if (Request.OPTIONS.equals(response.getHeader(CSeqHeader.NAME))) {
- return DBG_PING;
- }
- return DBG;
- } else if (evt instanceof RequestEvent) {
- if (isRequestEvent(Request.OPTIONS, evt)) {
- return DBG_PING;
- }
- return DBG;
- }
- return false;
- }
-
- private static String logEvt(EventObject evt) {
- if (evt instanceof RequestEvent) {
- return ((RequestEvent) evt).getRequest().toString();
- } else if (evt instanceof ResponseEvent) {
- return ((ResponseEvent) evt).getResponse().toString();
- } else {
- return evt.toString();
- }
- }
-
- private class RegisterCommand extends EventObject {
- private int mDuration;
-
- public RegisterCommand(int duration) {
- super(SipSessionGroup.this);
- mDuration = duration;
- }
-
- public int getDuration() {
- return mDuration;
- }
- }
-
- private class MakeCallCommand extends EventObject {
- private String mSessionDescription;
- private int mTimeout; // in seconds
-
- public MakeCallCommand(SipProfile peerProfile,
- String sessionDescription, int timeout) {
- super(peerProfile);
- mSessionDescription = sessionDescription;
- mTimeout = timeout;
- }
-
- public SipProfile getPeerProfile() {
- return (SipProfile) getSource();
- }
-
- public String getSessionDescription() {
- return mSessionDescription;
- }
-
- public int getTimeout() {
- return mTimeout;
- }
- }
-
- /** Class to help safely run KeepAliveProcessCallback in a different thread. */
- static class KeepAliveProcessCallbackProxy implements KeepAliveProcessCallback {
- private static final String KAPCP_TAG = "KeepAliveProcessCallbackProxy";
- private KeepAliveProcessCallback mCallback;
-
- KeepAliveProcessCallbackProxy(KeepAliveProcessCallback callback) {
- mCallback = callback;
- }
-
- private void proxy(Runnable runnable) {
- // 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, "SIP-KeepAliveProcessCallbackThread").start();
- }
-
- @Override
- public void onResponse(final boolean portChanged) {
- if (mCallback == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mCallback.onResponse(portChanged);
- } catch (Throwable t) {
- loge("onResponse", t);
- }
- }
- });
- }
-
- @Override
- public void onError(final int errorCode, final String description) {
- if (mCallback == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mCallback.onError(errorCode, description);
- } catch (Throwable t) {
- loge("onError", t);
- }
- }
- });
- }
-
- private void loge(String s, Throwable t) {
- Rlog.e(KAPCP_TAG, s, t);
- }
- }
-
- private void log(String s) {
- Rlog.d(TAG, s);
- }
-
- private void loge(String s, Throwable t) {
- Rlog.e(TAG, s, t);
- }
-}
diff --git a/voip/java/com/android/server/sip/SipSessionListenerProxy.java b/voip/java/com/android/server/sip/SipSessionListenerProxy.java
deleted file mode 100644
index 7a4ae8d..0000000
--- a/voip/java/com/android/server/sip/SipSessionListenerProxy.java
+++ /dev/null
@@ -1,265 +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 com.android.server.sip;
-
-import android.net.sip.ISipSession;
-import android.net.sip.ISipSessionListener;
-import android.net.sip.SipProfile;
-import android.os.DeadObjectException;
-import android.telephony.Rlog;
-
-/** Class to help safely run a callback in a different thread. */
-class SipSessionListenerProxy extends ISipSessionListener.Stub {
- private static final String TAG = "SipSessionListnerProxy";
-
- private ISipSessionListener mListener;
-
- public void setListener(ISipSessionListener listener) {
- mListener = listener;
- }
-
- public ISipSessionListener getListener() {
- return mListener;
- }
-
- private void proxy(Runnable runnable) {
- // 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, "SipSessionCallbackThread").start();
- }
-
- @Override
- public void onCalling(final ISipSession session) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onCalling(session);
- } catch (Throwable t) {
- handle(t, "onCalling()");
- }
- }
- });
- }
-
- @Override
- public void onRinging(final ISipSession session, final SipProfile caller,
- final String sessionDescription) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onRinging(session, caller, sessionDescription);
- } catch (Throwable t) {
- handle(t, "onRinging()");
- }
- }
- });
- }
-
- @Override
- public void onRingingBack(final ISipSession session) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onRingingBack(session);
- } catch (Throwable t) {
- handle(t, "onRingingBack()");
- }
- }
- });
- }
-
- @Override
- public void onCallEstablished(final ISipSession session,
- final String sessionDescription) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onCallEstablished(session, sessionDescription);
- } catch (Throwable t) {
- handle(t, "onCallEstablished()");
- }
- }
- });
- }
-
- @Override
- public void onCallEnded(final ISipSession session) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onCallEnded(session);
- } catch (Throwable t) {
- handle(t, "onCallEnded()");
- }
- }
- });
- }
-
- @Override
- public void onCallTransferring(final ISipSession newSession,
- final String sessionDescription) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onCallTransferring(newSession, sessionDescription);
- } catch (Throwable t) {
- handle(t, "onCallTransferring()");
- }
- }
- });
- }
-
- @Override
- public void onCallBusy(final ISipSession session) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onCallBusy(session);
- } catch (Throwable t) {
- handle(t, "onCallBusy()");
- }
- }
- });
- }
-
- @Override
- public void onCallChangeFailed(final ISipSession session,
- final int errorCode, final String message) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onCallChangeFailed(session, errorCode, message);
- } catch (Throwable t) {
- handle(t, "onCallChangeFailed()");
- }
- }
- });
- }
-
- @Override
- public void onError(final ISipSession session, final int errorCode,
- final String message) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onError(session, errorCode, message);
- } catch (Throwable t) {
- handle(t, "onError()");
- }
- }
- });
- }
-
- @Override
- public void onRegistering(final ISipSession session) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onRegistering(session);
- } catch (Throwable t) {
- handle(t, "onRegistering()");
- }
- }
- });
- }
-
- @Override
- public void onRegistrationDone(final ISipSession session,
- final int duration) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onRegistrationDone(session, duration);
- } catch (Throwable t) {
- handle(t, "onRegistrationDone()");
- }
- }
- });
- }
-
- @Override
- public void onRegistrationFailed(final ISipSession session,
- final int errorCode, final String message) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onRegistrationFailed(session, errorCode, message);
- } catch (Throwable t) {
- handle(t, "onRegistrationFailed()");
- }
- }
- });
- }
-
- @Override
- public void onRegistrationTimeout(final ISipSession session) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onRegistrationTimeout(session);
- } catch (Throwable t) {
- handle(t, "onRegistrationTimeout()");
- }
- }
- });
- }
-
- private void handle(Throwable t, String message) {
- if (t instanceof DeadObjectException) {
- mListener = null;
- // This creates race but it's harmless. Just don't log the error
- // when it happens.
- } else if (mListener != null) {
- loge(message, t);
- }
- }
-
- private void log(String s) {
- Rlog.d(TAG, s);
- }
-
- private void loge(String s, Throwable t) {
- Rlog.e(TAG, s, t);
- }
-}
diff --git a/voip/java/com/android/server/sip/SipWakeLock.java b/voip/java/com/android/server/sip/SipWakeLock.java
deleted file mode 100644
index b3fbb56..0000000
--- a/voip/java/com/android/server/sip/SipWakeLock.java
+++ /dev/null
@@ -1,73 +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 com.android.server.sip;
-
-import android.os.PowerManager;
-import android.telephony.Rlog;
-
-import java.util.HashSet;
-
-class SipWakeLock {
- private static final String TAG = "SipWakeLock";
- private static final boolean DBG = false;
- private PowerManager mPowerManager;
- private PowerManager.WakeLock mWakeLock;
- private PowerManager.WakeLock mTimerWakeLock;
- private HashSet<Object> mHolders = new HashSet<Object>();
-
- SipWakeLock(PowerManager powerManager) {
- mPowerManager = powerManager;
- }
-
- synchronized void reset() {
- if (DBG) log("reset count=" + mHolders.size());
- mHolders.clear();
- release(null);
- }
-
- synchronized void acquire(long timeout) {
- if (mTimerWakeLock == null) {
- mTimerWakeLock = mPowerManager.newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK, "SipWakeLock.timer");
- mTimerWakeLock.setReferenceCounted(true);
- }
- mTimerWakeLock.acquire(timeout);
- }
-
- synchronized void acquire(Object holder) {
- mHolders.add(holder);
- if (mWakeLock == null) {
- mWakeLock = mPowerManager.newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK, "SipWakeLock");
- }
- if (!mWakeLock.isHeld()) mWakeLock.acquire();
- if (DBG) log("acquire count=" + mHolders.size());
- }
-
- synchronized void release(Object holder) {
- mHolders.remove(holder);
- if ((mWakeLock != null) && mHolders.isEmpty()
- && mWakeLock.isHeld()) {
- mWakeLock.release();
- }
- if (DBG) log("release count=" + mHolders.size());
- }
-
- private void log(String s) {
- Rlog.d(TAG, s);
- }
-}
diff --git a/voip/java/com/android/server/sip/SipWakeupTimer.java b/voip/java/com/android/server/sip/SipWakeupTimer.java
deleted file mode 100644
index 3ba43312..0000000
--- a/voip/java/com/android/server/sip/SipWakeupTimer.java
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * Copyright (C) 2011, 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 com.android.server.sip;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.SystemClock;
-import android.telephony.Rlog;
-
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.TreeSet;
-import java.util.concurrent.Executor;
-
-/**
- * Timer that can schedule events to occur even when the device is in sleep.
- */
-class SipWakeupTimer extends BroadcastReceiver {
- private static final String TAG = "SipWakeupTimer";
- private static final boolean DBG = SipService.DBG && true; // STOPSHIP if true
- private static final String TRIGGER_TIME = "TriggerTime";
-
- private Context mContext;
- private AlarmManager mAlarmManager;
-
- // runnable --> time to execute in SystemClock
- private TreeSet<MyEvent> mEventQueue =
- new TreeSet<MyEvent>(new MyEventComparator());
-
- private PendingIntent mPendingIntent;
-
- private Executor mExecutor;
-
- public SipWakeupTimer(Context context, Executor executor) {
- mContext = context;
- mAlarmManager = (AlarmManager)
- context.getSystemService(Context.ALARM_SERVICE);
-
- IntentFilter filter = new IntentFilter(getAction());
- context.registerReceiver(this, filter);
- mExecutor = executor;
- }
-
- /**
- * Stops the timer. No event can be scheduled after this method is called.
- */
- public synchronized void stop() {
- mContext.unregisterReceiver(this);
- if (mPendingIntent != null) {
- mAlarmManager.cancel(mPendingIntent);
- mPendingIntent = null;
- }
- mEventQueue.clear();
- mEventQueue = null;
- }
-
- private boolean stopped() {
- if (mEventQueue == null) {
- if (DBG) log("Timer stopped");
- return true;
- } else {
- return false;
- }
- }
-
- private void cancelAlarm() {
- mAlarmManager.cancel(mPendingIntent);
- mPendingIntent = null;
- }
-
- private void recalculatePeriods() {
- if (mEventQueue.isEmpty()) return;
-
- MyEvent firstEvent = mEventQueue.first();
- int minPeriod = firstEvent.mMaxPeriod;
- long minTriggerTime = firstEvent.mTriggerTime;
- for (MyEvent e : mEventQueue) {
- e.mPeriod = e.mMaxPeriod / minPeriod * minPeriod;
- int interval = (int) (e.mLastTriggerTime + e.mMaxPeriod
- - minTriggerTime);
- interval = interval / minPeriod * minPeriod;
- e.mTriggerTime = minTriggerTime + interval;
- }
- TreeSet<MyEvent> newQueue = new TreeSet<MyEvent>(
- mEventQueue.comparator());
- newQueue.addAll(mEventQueue);
- mEventQueue.clear();
- mEventQueue = newQueue;
- if (DBG) {
- log("queue re-calculated");
- printQueue();
- }
- }
-
- // Determines the period and the trigger time of the new event and insert it
- // to the queue.
- private void insertEvent(MyEvent event) {
- long now = SystemClock.elapsedRealtime();
- if (mEventQueue.isEmpty()) {
- event.mTriggerTime = now + event.mPeriod;
- mEventQueue.add(event);
- return;
- }
- MyEvent firstEvent = mEventQueue.first();
- int minPeriod = firstEvent.mPeriod;
- if (minPeriod <= event.mMaxPeriod) {
- event.mPeriod = event.mMaxPeriod / minPeriod * minPeriod;
- int interval = event.mMaxPeriod;
- interval -= (int) (firstEvent.mTriggerTime - now);
- interval = interval / minPeriod * minPeriod;
- event.mTriggerTime = firstEvent.mTriggerTime + interval;
- mEventQueue.add(event);
- } else {
- long triggerTime = now + event.mPeriod;
- if (firstEvent.mTriggerTime < triggerTime) {
- event.mTriggerTime = firstEvent.mTriggerTime;
- event.mLastTriggerTime -= event.mPeriod;
- } else {
- event.mTriggerTime = triggerTime;
- }
- mEventQueue.add(event);
- recalculatePeriods();
- }
- }
-
- /**
- * Sets a periodic timer.
- *
- * @param period the timer period; in milli-second
- * @param callback is called back when the timer goes off; the same callback
- * can be specified in multiple timer events
- */
- public synchronized void set(int period, Runnable callback) {
- if (stopped()) return;
-
- long now = SystemClock.elapsedRealtime();
- MyEvent event = new MyEvent(period, callback, now);
- insertEvent(event);
-
- if (mEventQueue.first() == event) {
- if (mEventQueue.size() > 1) cancelAlarm();
- scheduleNext();
- }
-
- long triggerTime = event.mTriggerTime;
- if (DBG) {
- log("set: add event " + event + " scheduled on "
- + showTime(triggerTime) + " at " + showTime(now)
- + ", #events=" + mEventQueue.size());
- printQueue();
- }
- }
-
- /**
- * Cancels all the timer events with the specified callback.
- *
- * @param callback the callback
- */
- public synchronized void cancel(Runnable callback) {
- if (stopped() || mEventQueue.isEmpty()) return;
- if (DBG) log("cancel:" + callback);
-
- MyEvent firstEvent = mEventQueue.first();
- for (Iterator<MyEvent> iter = mEventQueue.iterator();
- iter.hasNext();) {
- MyEvent event = iter.next();
- if (event.mCallback == callback) {
- iter.remove();
- if (DBG) log(" cancel found:" + event);
- }
- }
- if (mEventQueue.isEmpty()) {
- cancelAlarm();
- } else if (mEventQueue.first() != firstEvent) {
- cancelAlarm();
- firstEvent = mEventQueue.first();
- firstEvent.mPeriod = firstEvent.mMaxPeriod;
- firstEvent.mTriggerTime = firstEvent.mLastTriggerTime
- + firstEvent.mPeriod;
- recalculatePeriods();
- scheduleNext();
- }
- if (DBG) {
- log("cancel: X");
- printQueue();
- }
- }
-
- private void scheduleNext() {
- if (stopped() || mEventQueue.isEmpty()) return;
-
- if (mPendingIntent != null) {
- throw new RuntimeException("pendingIntent is not null!");
- }
-
- MyEvent event = mEventQueue.first();
- Intent intent = new Intent(getAction());
- intent.putExtra(TRIGGER_TIME, event.mTriggerTime);
- PendingIntent pendingIntent = mPendingIntent =
- PendingIntent.getBroadcast(mContext, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- event.mTriggerTime, pendingIntent);
- }
-
- @Override
- public synchronized void onReceive(Context context, Intent intent) {
- // This callback is already protected by AlarmManager's wake lock.
- String action = intent.getAction();
- if (getAction().equals(action)
- && intent.getExtras().containsKey(TRIGGER_TIME)) {
- mPendingIntent = null;
- long triggerTime = intent.getLongExtra(TRIGGER_TIME, -1L);
- execute(triggerTime);
- } else {
- log("onReceive: unrecognized intent: " + intent);
- }
- }
-
- private void printQueue() {
- int count = 0;
- for (MyEvent event : mEventQueue) {
- log(" " + event + ": scheduled at "
- + showTime(event.mTriggerTime) + ": last at "
- + showTime(event.mLastTriggerTime));
- if (++count >= 5) break;
- }
- if (mEventQueue.size() > count) {
- log(" .....");
- } else if (count == 0) {
- log(" <empty>");
- }
- }
-
- private void execute(long triggerTime) {
- if (DBG) log("time's up, triggerTime = "
- + showTime(triggerTime) + ": " + mEventQueue.size());
- if (stopped() || mEventQueue.isEmpty()) return;
-
- for (MyEvent event : mEventQueue) {
- if (event.mTriggerTime != triggerTime) continue;
- if (DBG) log("execute " + event);
-
- event.mLastTriggerTime = triggerTime;
- event.mTriggerTime += event.mPeriod;
-
- // run the callback in the handler thread to prevent deadlock
- mExecutor.execute(event.mCallback);
- }
- if (DBG) {
- log("after timeout execution");
- printQueue();
- }
- scheduleNext();
- }
-
- private String getAction() {
- return toString();
- }
-
- private String showTime(long time) {
- int ms = (int) (time % 1000);
- int s = (int) (time / 1000);
- int m = s / 60;
- s %= 60;
- return String.format("%d.%d.%d", m, s, ms);
- }
-
- private static class MyEvent {
- int mPeriod;
- int mMaxPeriod;
- long mTriggerTime;
- long mLastTriggerTime;
- Runnable mCallback;
-
- MyEvent(int period, Runnable callback, long now) {
- mPeriod = mMaxPeriod = period;
- mCallback = callback;
- mLastTriggerTime = now;
- }
-
- @Override
- public String toString() {
- String s = super.toString();
- s = s.substring(s.indexOf("@"));
- return s + ":" + (mPeriod / 1000) + ":" + (mMaxPeriod / 1000) + ":"
- + toString(mCallback);
- }
-
- private String toString(Object o) {
- String s = o.toString();
- int index = s.indexOf("$");
- if (index > 0) s = s.substring(index + 1);
- return s;
- }
- }
-
- // Sort the events by mMaxPeriod so that the first event can be used to
- // align events with larger periods
- private static class MyEventComparator implements Comparator<MyEvent> {
- @Override
- public int compare(MyEvent e1, MyEvent e2) {
- if (e1 == e2) return 0;
- int diff = e1.mMaxPeriod - e2.mMaxPeriod;
- if (diff == 0) diff = -1;
- return diff;
- }
-
- @Override
- public boolean equals(Object that) {
- return (this == that);
- }
- }
-
- private void log(String s) {
- Rlog.d(TAG, s);
- }
-}
diff --git a/voip/jni/rtp/AmrCodec.cpp b/voip/jni/rtp/AmrCodec.cpp
deleted file mode 100644
index e2d820e..0000000
--- a/voip/jni/rtp/AmrCodec.cpp
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyrightm (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.
- */
-
-#include <string.h>
-
-#include "AudioCodec.h"
-
-#include "gsmamr_dec.h"
-#include "gsmamr_enc.h"
-
-namespace {
-
-const int gFrameBits[8] = {95, 103, 118, 134, 148, 159, 204, 244};
-
-//------------------------------------------------------------------------------
-
-// See RFC 4867 for the encoding details.
-
-class AmrCodec : public AudioCodec
-{
-public:
- AmrCodec() {
- if (AMREncodeInit(&mEncoder, &mSidSync, false)) {
- mEncoder = NULL;
- }
- if (GSMInitDecode(&mDecoder, (Word8 *)"RTP")) {
- mDecoder = NULL;
- }
- }
-
- ~AmrCodec() {
- if (mEncoder) {
- AMREncodeExit(&mEncoder, &mSidSync);
- }
- if (mDecoder) {
- GSMDecodeFrameExit(&mDecoder);
- }
- }
-
- int set(int sampleRate, const char *fmtp);
- int encode(void *payload, int16_t *samples);
- int decode(int16_t *samples, int count, void *payload, int length);
-
-private:
- void *mEncoder;
- void *mSidSync;
- void *mDecoder;
-
- int mMode;
- int mModeSet;
- bool mOctetAligned;
-};
-
-int AmrCodec::set(int sampleRate, const char *fmtp)
-{
- // These parameters are not supported.
- if (strcasestr(fmtp, "crc=1") || strcasestr(fmtp, "robust-sorting=1") ||
- strcasestr(fmtp, "interleaving=")) {
- return -1;
- }
-
- // Handle mode-set and octet-align.
- const char *modes = strcasestr(fmtp, "mode-set=");
- if (modes) {
- mMode = 0;
- mModeSet = 0;
- for (char c = *modes; c && c != ' '; c = *++modes) {
- if (c >= '0' && c <= '7') {
- int mode = c - '0';
- if (mode > mMode) {
- mMode = mode;
- }
- mModeSet |= 1 << mode;
- }
- }
- } else {
- mMode = 7;
- mModeSet = 0xFF;
- }
- mOctetAligned = (strcasestr(fmtp, "octet-align=1") != NULL);
-
- // TODO: handle mode-change-*.
-
- return (sampleRate == 8000 && mEncoder && mDecoder) ? 160 : -1;
-}
-
-int AmrCodec::encode(void *payload, int16_t *samples)
-{
- unsigned char *bytes = (unsigned char *)payload;
- Frame_Type_3GPP type;
-
- int length = AMREncode(mEncoder, mSidSync, (Mode)mMode,
- samples, bytes + 1, &type, AMR_TX_WMF);
-
- if (type != mMode || length != (8 + gFrameBits[mMode] + 7) >> 3) {
- return -1;
- }
-
- if (mOctetAligned) {
- bytes[0] = 0xF0;
- bytes[1] = (mMode << 3) | 0x04;
- ++length;
- } else {
- // CMR = 15 (4-bit), F = 0 (1-bit), FT = mMode (4-bit), Q = 1 (1-bit).
- bytes[0] = 0xFF;
- bytes[1] = 0xC0 | (mMode << 1) | 1;
-
- // Shift left 6 bits and update the length.
- bytes[length + 1] = 0;
- for (int i = 0; i <= length; ++i) {
- bytes[i] = (bytes[i] << 6) | (bytes[i + 1] >> 2);
- }
- length = (10 + gFrameBits[mMode] + 7) >> 3;
- }
- return length;
-}
-
-int AmrCodec::decode(int16_t *samples, int count, void *payload, int length)
-{
- unsigned char *bytes = (unsigned char *)payload;
- Frame_Type_3GPP type;
- if (length < 2) {
- return -1;
- }
- int request = bytes[0] >> 4;
-
- if (mOctetAligned) {
- if ((bytes[1] & 0xC4) != 0x04) {
- return -1;
- }
- type = (Frame_Type_3GPP)(bytes[1] >> 3);
- if (length != (16 + gFrameBits[type] + 7) >> 3) {
- return -1;
- }
- length -= 2;
- bytes += 2;
- } else {
- if ((bytes[0] & 0x0C) || !(bytes[1] & 0x40)) {
- return -1;
- }
- type = (Frame_Type_3GPP)((bytes[0] << 1 | bytes[1] >> 7) & 0x07);
- if (length != (10 + gFrameBits[type] + 7) >> 3) {
- return -1;
- }
-
- // Shift left 2 bits and update the length.
- --length;
- for (int i = 1; i < length; ++i) {
- bytes[i] = (bytes[i] << 2) | (bytes[i + 1] >> 6);
- }
- bytes[length] <<= 2;
- length = (gFrameBits[type] + 7) >> 3;
- ++bytes;
- }
-
- if (AMRDecode(mDecoder, type, bytes, samples, MIME_IETF) != length) {
- return -1;
- }
-
- // Handle CMR
- if (request < 8 && request != mMode) {
- for (int i = request; i >= 0; --i) {
- if (mModeSet & (1 << i)) {
- mMode = request;
- break;
- }
- }
- }
-
- return 160;
-}
-
-//------------------------------------------------------------------------------
-
-// See RFC 3551 for the encoding details.
-
-class GsmEfrCodec : public AudioCodec
-{
-public:
- GsmEfrCodec() {
- if (AMREncodeInit(&mEncoder, &mSidSync, false)) {
- mEncoder = NULL;
- }
- if (GSMInitDecode(&mDecoder, (Word8 *)"RTP")) {
- mDecoder = NULL;
- }
- }
-
- ~GsmEfrCodec() {
- if (mEncoder) {
- AMREncodeExit(&mEncoder, &mSidSync);
- }
- if (mDecoder) {
- GSMDecodeFrameExit(&mDecoder);
- }
- }
-
- int set(int sampleRate, const char *fmtp) {
- return (sampleRate == 8000 && mEncoder && mDecoder) ? 160 : -1;
- }
-
- int encode(void *payload, int16_t *samples);
- int decode(int16_t *samples, int count, void *payload, int length);
-
-private:
- void *mEncoder;
- void *mSidSync;
- void *mDecoder;
-};
-
-int GsmEfrCodec::encode(void *payload, int16_t *samples)
-{
- unsigned char *bytes = (unsigned char *)payload;
- Frame_Type_3GPP type;
-
- int length = AMREncode(mEncoder, mSidSync, MR122,
- samples, bytes, &type, AMR_TX_WMF);
-
- if (type == AMR_122 && length == 32) {
- bytes[0] = 0xC0 | (bytes[1] >> 4);
- for (int i = 1; i < 31; ++i) {
- bytes[i] = (bytes[i] << 4) | (bytes[i + 1] >> 4);
- }
- return 31;
- }
- return -1;
-}
-
-int GsmEfrCodec::decode(int16_t *samples, int count, void *payload, int length)
-{
- unsigned char *bytes = (unsigned char *)payload;
- int n = 0;
- while (n + 160 <= count && length >= 31 && (bytes[0] >> 4) == 0x0C) {
- for (int i = 0; i < 30; ++i) {
- bytes[i] = (bytes[i] << 4) | (bytes[i + 1] >> 4);
- }
- bytes[30] <<= 4;
-
- if (AMRDecode(mDecoder, AMR_122, bytes, &samples[n], MIME_IETF) != 31) {
- break;
- }
- n += 160;
- length -= 31;
- bytes += 31;
- }
- return n;
-}
-
-} // namespace
-
-AudioCodec *newAmrCodec()
-{
- return new AmrCodec;
-}
-
-AudioCodec *newGsmEfrCodec()
-{
- return new GsmEfrCodec;
-}
diff --git a/voip/jni/rtp/Android.mk b/voip/jni/rtp/Android.mk
deleted file mode 100644
index b265cdd..0000000
--- a/voip/jni/rtp/Android.mk
+++ /dev/null
@@ -1,59 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := librtp_jni
-
-LOCAL_SRC_FILES := \
- AudioCodec.cpp \
- AudioGroup.cpp \
- EchoSuppressor.cpp \
- RtpStream.cpp \
- util.cpp \
- rtp_jni.cpp
-
-LOCAL_SRC_FILES += \
- AmrCodec.cpp \
- G711Codec.cpp \
- GsmCodec.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libnativehelper \
- libcutils \
- libutils \
- libmedia \
- libstagefright_amrnb_common
-
-LOCAL_STATIC_LIBRARIES := libgsm libstagefright_amrnbdec libstagefright_amrnbenc
-
-LOCAL_C_INCLUDES += \
- $(JNI_H_INCLUDE) \
- external/libgsm/inc \
- frameworks/av/media/libstagefright/codecs/amrnb/common/include \
- frameworks/av/media/libstagefright/codecs/amrnb/common/ \
- frameworks/av/media/libstagefright/codecs/amrnb/enc/include \
- frameworks/av/media/libstagefright/codecs/amrnb/enc/src \
- frameworks/av/media/libstagefright/codecs/amrnb/dec/include \
- frameworks/av/media/libstagefright/codecs/amrnb/dec/src \
- $(call include-path-for, audio-effects)
-
-LOCAL_CFLAGS += -fvisibility=hidden
-
-
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/voip/jni/rtp/AudioCodec.cpp b/voip/jni/rtp/AudioCodec.cpp
deleted file mode 100644
index c75fbc9..0000000
--- a/voip/jni/rtp/AudioCodec.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyrightm (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.
- */
-
-#include <strings.h>
-
-#include "AudioCodec.h"
-
-extern AudioCodec *newAlawCodec();
-extern AudioCodec *newUlawCodec();
-extern AudioCodec *newGsmCodec();
-extern AudioCodec *newAmrCodec();
-extern AudioCodec *newGsmEfrCodec();
-
-struct AudioCodecType {
- const char *name;
- AudioCodec *(*create)();
-} gAudioCodecTypes[] = {
- {"PCMA", newAlawCodec},
- {"PCMU", newUlawCodec},
- {"GSM", newGsmCodec},
- {"AMR", newAmrCodec},
- {"GSM-EFR", newGsmEfrCodec},
- {NULL, NULL},
-};
-
-AudioCodec *newAudioCodec(const char *codecName)
-{
- AudioCodecType *type = gAudioCodecTypes;
- while (type->name != NULL) {
- if (strcasecmp(codecName, type->name) == 0) {
- AudioCodec *codec = type->create();
- codec->name = type->name;
- return codec;
- }
- ++type;
- }
- return NULL;
-}
diff --git a/voip/jni/rtp/AudioCodec.h b/voip/jni/rtp/AudioCodec.h
deleted file mode 100644
index 741730b..0000000
--- a/voip/jni/rtp/AudioCodec.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyrightm (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.
- */
-
-#include <stdint.h>
-
-#ifndef __AUDIO_CODEC_H__
-#define __AUDIO_CODEC_H__
-
-class AudioCodec
-{
-public:
- const char *name;
- // Needed by destruction through base class pointers.
- virtual ~AudioCodec() {}
- // Returns sampleCount or non-positive value if unsupported.
- virtual int set(int sampleRate, const char *fmtp) = 0;
- // Returns the length of payload in bytes.
- virtual int encode(void *payload, int16_t *samples) = 0;
- // Returns the number of decoded samples.
- virtual int decode(int16_t *samples, int count, void *payload, int length) = 0;
-};
-
-AudioCodec *newAudioCodec(const char *codecName);
-
-#endif
diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp
deleted file mode 100644
index 2f0829e..0000000
--- a/voip/jni/rtp/AudioGroup.cpp
+++ /dev/null
@@ -1,1073 +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.
- */
-
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/epoll.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <time.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-
-// #define LOG_NDEBUG 0
-#define LOG_TAG "AudioGroup"
-#include <cutils/atomic.h>
-#include <cutils/properties.h>
-#include <utils/Log.h>
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/threads.h>
-#include <utils/SystemClock.h>
-#include <media/AudioSystem.h>
-#include <media/AudioRecord.h>
-#include <media/AudioTrack.h>
-#include <media/mediarecorder.h>
-#include <media/AudioEffect.h>
-#include <audio_effects/effect_aec.h>
-#include <system/audio.h>
-
-#include "jni.h"
-#include "JNIHelp.h"
-
-#include "AudioCodec.h"
-#include "EchoSuppressor.h"
-
-extern int parse(JNIEnv *env, jstring jAddress, int port, sockaddr_storage *ss);
-
-namespace {
-
-using namespace android;
-
-int gRandom = -1;
-
-// We use a circular array to implement jitter buffer. The simplest way is doing
-// a modulo operation on the index while accessing the array. However modulo can
-// be expensive on some platforms, such as ARM. Thus we round up the size of the
-// array to the nearest power of 2 and then use bitwise-and instead of modulo.
-// Currently we make it 2048ms long and assume packet interval is 50ms or less.
-// The first 100ms is the place where samples get mixed. The rest is the real
-// jitter buffer. For a stream at 8000Hz it takes 32 kilobytes. These numbers
-// are chosen by experiments and each of them can be adjusted as needed.
-
-// Originally a stream does not send packets when it is receive-only or there is
-// nothing to mix. However, this causes some problems with certain firewalls and
-// proxies. A firewall might remove a port mapping when there is no outgoing
-// packet for a preiod of time, and a proxy might wait for incoming packets from
-// both sides before start forwarding. To solve these problems, we send out a
-// silence packet on the stream for every second. It should be good enough to
-// keep the stream alive with relatively low resources.
-
-// Other notes:
-// + We use elapsedRealtime() to get the time. Since we use 32bit variables
-// instead of 64bit ones, comparison must be done by subtraction.
-// + Sampling rate must be multiple of 1000Hz, and packet length must be in
-// milliseconds. No floating points.
-// + If we cannot get enough CPU, we drop samples and simulate packet loss.
-// + Resampling is not done yet, so streams in one group must use the same rate.
-// For the first release only 8000Hz is supported.
-
-#define BUFFER_SIZE 2048
-#define HISTORY_SIZE 100
-#define MEASURE_BASE 100
-#define MEASURE_PERIOD 5000
-#define DTMF_PERIOD 200
-
-class AudioStream
-{
-public:
- AudioStream();
- ~AudioStream();
- bool set(int mode, int socket, sockaddr_storage *remote,
- AudioCodec *codec, int sampleRate, int sampleCount,
- int codecType, int dtmfType);
-
- void sendDtmf(int event);
- bool mix(int32_t *output, int head, int tail, int sampleRate);
- void encode(int tick, AudioStream *chain);
- void decode(int tick);
-
-private:
- enum {
- NORMAL = 0,
- SEND_ONLY = 1,
- RECEIVE_ONLY = 2,
- LAST_MODE = 2,
- };
-
- int mMode;
- int mSocket;
- sockaddr_storage mRemote;
- AudioCodec *mCodec;
- uint32_t mCodecMagic;
- uint32_t mDtmfMagic;
- bool mFixRemote;
-
- int mTick;
- int mSampleRate;
- int mSampleCount;
- int mInterval;
- int mKeepAlive;
-
- int16_t *mBuffer;
- int mBufferMask;
- int mBufferHead;
- int mBufferTail;
- int mLatencyTimer;
- int mLatencyScore;
-
- uint16_t mSequence;
- uint32_t mTimestamp;
- uint32_t mSsrc;
-
- int mDtmfEvent;
- int mDtmfStart;
-
- AudioStream *mNext;
-
- friend class AudioGroup;
-};
-
-AudioStream::AudioStream()
-{
- mSocket = -1;
- mCodec = NULL;
- mBuffer = NULL;
- mNext = NULL;
-}
-
-AudioStream::~AudioStream()
-{
- close(mSocket);
- delete mCodec;
- delete [] mBuffer;
- ALOGD("stream[%d] is dead", mSocket);
-}
-
-bool AudioStream::set(int mode, int socket, sockaddr_storage *remote,
- AudioCodec *codec, int sampleRate, int sampleCount,
- int codecType, int dtmfType)
-{
- if (mode < 0 || mode > LAST_MODE) {
- return false;
- }
- mMode = mode;
-
- mCodecMagic = (0x8000 | codecType) << 16;
- mDtmfMagic = (dtmfType == -1) ? 0 : (0x8000 | dtmfType) << 16;
-
- mTick = elapsedRealtime();
- mSampleRate = sampleRate / 1000;
- mSampleCount = sampleCount;
- mInterval = mSampleCount / mSampleRate;
-
- // Allocate jitter buffer.
- for (mBufferMask = 8; mBufferMask < mSampleRate; mBufferMask <<= 1);
- mBufferMask *= BUFFER_SIZE;
- mBuffer = new int16_t[mBufferMask];
- --mBufferMask;
- mBufferHead = 0;
- mBufferTail = 0;
- mLatencyTimer = 0;
- mLatencyScore = 0;
-
- // Initialize random bits.
- read(gRandom, &mSequence, sizeof(mSequence));
- read(gRandom, &mTimestamp, sizeof(mTimestamp));
- read(gRandom, &mSsrc, sizeof(mSsrc));
-
- mDtmfEvent = -1;
- mDtmfStart = 0;
-
- // Only take over these things when succeeded.
- mSocket = socket;
- if (codec) {
- mRemote = *remote;
- mCodec = codec;
-
- // Here we should never get an private address, but some buggy proxy
- // servers do give us one. To solve this, we replace the address when
- // the first time we successfully decode an incoming packet.
- mFixRemote = false;
- if (remote->ss_family == AF_INET) {
- unsigned char *address =
- (unsigned char *)&((sockaddr_in *)remote)->sin_addr;
- if (address[0] == 10 ||
- (address[0] == 172 && (address[1] >> 4) == 1) ||
- (address[0] == 192 && address[1] == 168)) {
- mFixRemote = true;
- }
- }
- }
-
- ALOGD("stream[%d] is configured as %s %dkHz %dms mode %d", mSocket,
- (codec ? codec->name : "RAW"), mSampleRate, mInterval, mMode);
- return true;
-}
-
-void AudioStream::sendDtmf(int event)
-{
- if (mDtmfMagic != 0) {
- mDtmfEvent = event << 24;
- mDtmfStart = mTimestamp + mSampleCount;
- }
-}
-
-bool AudioStream::mix(int32_t *output, int head, int tail, int sampleRate)
-{
- if (mMode == SEND_ONLY) {
- return false;
- }
-
- if (head - mBufferHead < 0) {
- head = mBufferHead;
- }
- if (tail - mBufferTail > 0) {
- tail = mBufferTail;
- }
- if (tail - head <= 0) {
- return false;
- }
-
- head *= mSampleRate;
- tail *= mSampleRate;
-
- if (sampleRate == mSampleRate) {
- for (int i = head; i - tail < 0; ++i) {
- output[i - head] += mBuffer[i & mBufferMask];
- }
- } else {
- // TODO: implement resampling.
- return false;
- }
- return true;
-}
-
-void AudioStream::encode(int tick, AudioStream *chain)
-{
- if (tick - mTick >= mInterval) {
- // We just missed the train. Pretend that packets in between are lost.
- int skipped = (tick - mTick) / mInterval;
- mTick += skipped * mInterval;
- mSequence += skipped;
- mTimestamp += skipped * mSampleCount;
- ALOGV("stream[%d] skips %d packets", mSocket, skipped);
- }
-
- tick = mTick;
- mTick += mInterval;
- ++mSequence;
- mTimestamp += mSampleCount;
-
- // If there is an ongoing DTMF event, send it now.
- if (mMode != RECEIVE_ONLY && mDtmfEvent != -1) {
- int duration = mTimestamp - mDtmfStart;
- // Make sure duration is reasonable.
- if (duration >= 0 && duration < mSampleRate * DTMF_PERIOD) {
- duration += mSampleCount;
- int32_t buffer[4] = {
- htonl(mDtmfMagic | mSequence),
- htonl(mDtmfStart),
- mSsrc,
- htonl(mDtmfEvent | duration),
- };
- if (duration >= mSampleRate * DTMF_PERIOD) {
- buffer[3] |= htonl(1 << 23);
- mDtmfEvent = -1;
- }
- sendto(mSocket, buffer, sizeof(buffer), MSG_DONTWAIT,
- (sockaddr *)&mRemote, sizeof(mRemote));
- return;
- }
- mDtmfEvent = -1;
- }
-
- int32_t buffer[mSampleCount + 3];
- bool data = false;
- if (mMode != RECEIVE_ONLY) {
- // Mix all other streams.
- memset(buffer, 0, sizeof(buffer));
- while (chain) {
- if (chain != this) {
- data |= chain->mix(buffer, tick - mInterval, tick, mSampleRate);
- }
- chain = chain->mNext;
- }
- }
-
- int16_t samples[mSampleCount];
- if (data) {
- // Saturate into 16 bits.
- for (int i = 0; i < mSampleCount; ++i) {
- int32_t sample = buffer[i];
- if (sample < -32768) {
- sample = -32768;
- }
- if (sample > 32767) {
- sample = 32767;
- }
- samples[i] = sample;
- }
- } else {
- if ((mTick ^ mKeepAlive) >> 10 == 0) {
- return;
- }
- mKeepAlive = mTick;
- memset(samples, 0, sizeof(samples));
-
- if (mMode != RECEIVE_ONLY) {
- ALOGV("stream[%d] no data", mSocket);
- }
- }
-
- if (!mCodec) {
- // Special case for device stream.
- send(mSocket, samples, sizeof(samples), MSG_DONTWAIT);
- return;
- }
-
- // Cook the packet and send it out.
- buffer[0] = htonl(mCodecMagic | mSequence);
- buffer[1] = htonl(mTimestamp);
- buffer[2] = mSsrc;
- int length = mCodec->encode(&buffer[3], samples);
- if (length <= 0) {
- ALOGV("stream[%d] encoder error", mSocket);
- return;
- }
- sendto(mSocket, buffer, length + 12, MSG_DONTWAIT, (sockaddr *)&mRemote,
- sizeof(mRemote));
-}
-
-void AudioStream::decode(int tick)
-{
- char c;
- if (mMode == SEND_ONLY) {
- recv(mSocket, &c, 1, MSG_DONTWAIT);
- return;
- }
-
- // Make sure mBufferHead and mBufferTail are reasonable.
- if ((unsigned int)(tick + BUFFER_SIZE - mBufferHead) > BUFFER_SIZE * 2) {
- mBufferHead = tick - HISTORY_SIZE;
- mBufferTail = mBufferHead;
- }
-
- if (tick - mBufferHead > HISTORY_SIZE) {
- // Throw away outdated samples.
- mBufferHead = tick - HISTORY_SIZE;
- if (mBufferTail - mBufferHead < 0) {
- mBufferTail = mBufferHead;
- }
- }
-
- // Adjust the jitter buffer if the latency keeps larger than the threshold
- // in the measurement period.
- int score = mBufferTail - tick - MEASURE_BASE;
- if (mLatencyScore > score || mLatencyScore <= 0) {
- mLatencyScore = score;
- mLatencyTimer = tick;
- } else if (tick - mLatencyTimer >= MEASURE_PERIOD) {
- ALOGV("stream[%d] reduces latency of %dms", mSocket, mLatencyScore);
- mBufferTail -= mLatencyScore;
- mLatencyScore = -1;
- }
-
- int count = (BUFFER_SIZE - (mBufferTail - mBufferHead)) * mSampleRate;
- if (count < mSampleCount) {
- // Buffer overflow. Drop the packet.
- ALOGV("stream[%d] buffer overflow", mSocket);
- recv(mSocket, &c, 1, MSG_DONTWAIT);
- return;
- }
-
- // Receive the packet and decode it.
- int16_t samples[count];
- if (!mCodec) {
- // Special case for device stream.
- count = recv(mSocket, samples, sizeof(samples),
- MSG_TRUNC | MSG_DONTWAIT) >> 1;
- } else {
- __attribute__((aligned(4))) uint8_t buffer[2048];
- sockaddr_storage remote;
- socklen_t addrlen = sizeof(remote);
-
- int length = recvfrom(mSocket, buffer, sizeof(buffer),
- MSG_TRUNC | MSG_DONTWAIT, (sockaddr *)&remote, &addrlen);
-
- // Do we need to check SSRC, sequence, and timestamp? They are not
- // reliable but at least they can be used to identify duplicates?
- if (length < 12 || length > (int)sizeof(buffer) ||
- (ntohl(*(uint32_t *)buffer) & 0xC07F0000) != mCodecMagic) {
- ALOGV("stream[%d] malformed packet", mSocket);
- return;
- }
- int offset = 12 + ((buffer[0] & 0x0F) << 2);
- if ((buffer[0] & 0x10) != 0) {
- offset += 4 + (ntohs(*(uint16_t *)&buffer[offset + 2]) << 2);
- }
- if ((buffer[0] & 0x20) != 0) {
- length -= buffer[length - 1];
- }
- length -= offset;
- if (length >= 0) {
- length = mCodec->decode(samples, count, &buffer[offset], length);
- }
- if (length > 0 && mFixRemote) {
- mRemote = remote;
- mFixRemote = false;
- }
- count = length;
- }
- if (count <= 0) {
- ALOGV("stream[%d] decoder error", mSocket);
- return;
- }
-
- if (tick - mBufferTail > 0) {
- // Buffer underrun. Reset the jitter buffer.
- ALOGV("stream[%d] buffer underrun", mSocket);
- if (mBufferTail - mBufferHead <= 0) {
- mBufferHead = tick + mInterval;
- mBufferTail = mBufferHead;
- } else {
- int tail = (tick + mInterval) * mSampleRate;
- for (int i = mBufferTail * mSampleRate; i - tail < 0; ++i) {
- mBuffer[i & mBufferMask] = 0;
- }
- mBufferTail = tick + mInterval;
- }
- }
-
- // Append to the jitter buffer.
- int tail = mBufferTail * mSampleRate;
- for (int i = 0; i < count; ++i) {
- mBuffer[tail & mBufferMask] = samples[i];
- ++tail;
- }
- mBufferTail += mInterval;
-}
-
-//------------------------------------------------------------------------------
-
-class AudioGroup
-{
-public:
- AudioGroup();
- ~AudioGroup();
- bool set(int sampleRate, int sampleCount);
-
- bool setMode(int mode);
- bool sendDtmf(int event);
- bool add(AudioStream *stream);
- bool remove(AudioStream *stream);
- bool platformHasAec() { return mPlatformHasAec; }
-
-private:
- enum {
- ON_HOLD = 0,
- MUTED = 1,
- NORMAL = 2,
- ECHO_SUPPRESSION = 3,
- LAST_MODE = 3,
- };
-
- bool checkPlatformAec();
-
- AudioStream *mChain;
- int mEventQueue;
- volatile int mDtmfEvent;
-
- int mMode;
- int mSampleRate;
- int mSampleCount;
- int mDeviceSocket;
- bool mPlatformHasAec;
-
- class NetworkThread : public Thread
- {
- public:
- NetworkThread(AudioGroup *group) : Thread(false), mGroup(group) {}
-
- bool start()
- {
- if (run("Network", ANDROID_PRIORITY_AUDIO) != NO_ERROR) {
- ALOGE("cannot start network thread");
- return false;
- }
- return true;
- }
-
- private:
- AudioGroup *mGroup;
- bool threadLoop();
- };
- sp<NetworkThread> mNetworkThread;
-
- class DeviceThread : public Thread
- {
- public:
- DeviceThread(AudioGroup *group) : Thread(false), mGroup(group) {}
-
- bool start()
- {
- if (run("Device", ANDROID_PRIORITY_AUDIO) != NO_ERROR) {
- ALOGE("cannot start device thread");
- return false;
- }
- return true;
- }
-
- private:
- AudioGroup *mGroup;
- bool threadLoop();
- };
- sp<DeviceThread> mDeviceThread;
-};
-
-AudioGroup::AudioGroup()
-{
- mMode = ON_HOLD;
- mChain = NULL;
- mEventQueue = -1;
- mDtmfEvent = -1;
- mDeviceSocket = -1;
- mNetworkThread = new NetworkThread(this);
- mDeviceThread = new DeviceThread(this);
- mPlatformHasAec = checkPlatformAec();
-}
-
-AudioGroup::~AudioGroup()
-{
- mNetworkThread->requestExitAndWait();
- mDeviceThread->requestExitAndWait();
- close(mEventQueue);
- close(mDeviceSocket);
- while (mChain) {
- AudioStream *next = mChain->mNext;
- delete mChain;
- mChain = next;
- }
- ALOGD("group[%d] is dead", mDeviceSocket);
-}
-
-bool AudioGroup::set(int sampleRate, int sampleCount)
-{
- mEventQueue = epoll_create(2);
- if (mEventQueue == -1) {
- ALOGE("epoll_create: %s", strerror(errno));
- return false;
- }
-
- mSampleRate = sampleRate;
- mSampleCount = sampleCount;
-
- // Create device socket.
- int pair[2];
- if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair)) {
- ALOGE("socketpair: %s", strerror(errno));
- return false;
- }
- mDeviceSocket = pair[0];
-
- // Create device stream.
- mChain = new AudioStream;
- if (!mChain->set(AudioStream::NORMAL, pair[1], NULL, NULL,
- sampleRate, sampleCount, -1, -1)) {
- close(pair[1]);
- ALOGE("cannot initialize device stream");
- return false;
- }
-
- // Give device socket a reasonable timeout.
- timeval tv;
- tv.tv_sec = 0;
- tv.tv_usec = 1000 * sampleCount / sampleRate * 500;
- if (setsockopt(pair[0], SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) {
- ALOGE("setsockopt: %s", strerror(errno));
- return false;
- }
-
- // Add device stream into event queue.
- epoll_event event;
- event.events = EPOLLIN;
- event.data.ptr = mChain;
- if (epoll_ctl(mEventQueue, EPOLL_CTL_ADD, pair[1], &event)) {
- ALOGE("epoll_ctl: %s", strerror(errno));
- return false;
- }
-
- // Anything else?
- ALOGD("stream[%d] joins group[%d]", pair[1], pair[0]);
- return true;
-}
-
-bool AudioGroup::setMode(int mode)
-{
- if (mode < 0 || mode > LAST_MODE) {
- return false;
- }
- // FIXME: temporary code to overcome echo and mic gain issues on herring and tuna boards.
- // Must be modified/removed when the root cause of the issue is fixed in the hardware or
- // driver
- char value[PROPERTY_VALUE_MAX];
- property_get("ro.product.board", value, "");
- if (mode == NORMAL &&
- (!strcmp(value, "herring") || !strcmp(value, "tuna"))) {
- mode = ECHO_SUPPRESSION;
- }
- if (mMode == mode) {
- return true;
- }
-
- mDeviceThread->requestExitAndWait();
- ALOGD("group[%d] switches from mode %d to %d", mDeviceSocket, mMode, mode);
- mMode = mode;
- return (mode == ON_HOLD) || mDeviceThread->start();
-}
-
-bool AudioGroup::sendDtmf(int event)
-{
- if (event < 0 || event > 15) {
- return false;
- }
-
- // DTMF is rarely used, so we try to make it as lightweight as possible.
- // Using volatile might be dodgy, but using a pipe or pthread primitives
- // or stop-set-restart threads seems too heavy. Will investigate later.
- timespec ts;
- ts.tv_sec = 0;
- ts.tv_nsec = 100000000;
- for (int i = 0; mDtmfEvent != -1 && i < 20; ++i) {
- nanosleep(&ts, NULL);
- }
- if (mDtmfEvent != -1) {
- return false;
- }
- mDtmfEvent = event;
- nanosleep(&ts, NULL);
- return true;
-}
-
-bool AudioGroup::add(AudioStream *stream)
-{
- mNetworkThread->requestExitAndWait();
-
- epoll_event event;
- event.events = EPOLLIN;
- event.data.ptr = stream;
- if (epoll_ctl(mEventQueue, EPOLL_CTL_ADD, stream->mSocket, &event)) {
- ALOGE("epoll_ctl: %s", strerror(errno));
- return false;
- }
-
- stream->mNext = mChain->mNext;
- mChain->mNext = stream;
- if (!mNetworkThread->start()) {
- // Only take over the stream when succeeded.
- mChain->mNext = stream->mNext;
- return false;
- }
-
- ALOGD("stream[%d] joins group[%d]", stream->mSocket, mDeviceSocket);
- return true;
-}
-
-bool AudioGroup::remove(AudioStream *stream)
-{
- mNetworkThread->requestExitAndWait();
-
- for (AudioStream *chain = mChain; chain->mNext; chain = chain->mNext) {
- if (chain->mNext == stream) {
- if (epoll_ctl(mEventQueue, EPOLL_CTL_DEL, stream->mSocket, NULL)) {
- ALOGE("epoll_ctl: %s", strerror(errno));
- return false;
- }
- chain->mNext = stream->mNext;
- ALOGD("stream[%d] leaves group[%d]", stream->mSocket, mDeviceSocket);
- delete stream;
- break;
- }
- }
-
- // Do not start network thread if there is only one stream.
- if (!mChain->mNext || !mNetworkThread->start()) {
- return false;
- }
- return true;
-}
-
-bool AudioGroup::NetworkThread::threadLoop()
-{
- AudioStream *chain = mGroup->mChain;
- int tick = elapsedRealtime();
- int deadline = tick + 10;
- int count = 0;
-
- for (AudioStream *stream = chain; stream; stream = stream->mNext) {
- if (tick - stream->mTick >= 0) {
- stream->encode(tick, chain);
- }
- if (deadline - stream->mTick > 0) {
- deadline = stream->mTick;
- }
- ++count;
- }
-
- int event = mGroup->mDtmfEvent;
- if (event != -1) {
- for (AudioStream *stream = chain; stream; stream = stream->mNext) {
- stream->sendDtmf(event);
- }
- mGroup->mDtmfEvent = -1;
- }
-
- deadline -= tick;
- if (deadline < 1) {
- deadline = 1;
- }
-
- epoll_event events[count];
- count = epoll_wait(mGroup->mEventQueue, events, count, deadline);
- if (count == -1) {
- ALOGE("epoll_wait: %s", strerror(errno));
- return false;
- }
- for (int i = 0; i < count; ++i) {
- ((AudioStream *)events[i].data.ptr)->decode(tick);
- }
-
- return true;
-}
-
-bool AudioGroup::checkPlatformAec()
-{
- effect_descriptor_t fxDesc;
- uint32_t numFx;
-
- if (AudioEffect::queryNumberEffects(&numFx) != NO_ERROR) {
- return false;
- }
- for (uint32_t i = 0; i < numFx; i++) {
- if (AudioEffect::queryEffect(i, &fxDesc) != NO_ERROR) {
- continue;
- }
- if (memcmp(&fxDesc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) {
- return true;
- }
- }
- return false;
-}
-
-bool AudioGroup::DeviceThread::threadLoop()
-{
- int mode = mGroup->mMode;
- int sampleRate = mGroup->mSampleRate;
- int sampleCount = mGroup->mSampleCount;
- int deviceSocket = mGroup->mDeviceSocket;
-
- // Find out the frame count for AudioTrack and AudioRecord.
- size_t output = 0;
- size_t input = 0;
- if (AudioTrack::getMinFrameCount(&output, AUDIO_STREAM_VOICE_CALL,
- sampleRate) != NO_ERROR || output <= 0 ||
- AudioRecord::getMinFrameCount(&input, sampleRate,
- AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_MONO) != NO_ERROR || input <= 0) {
- ALOGE("cannot compute frame count");
- return false;
- }
- ALOGD("reported frame count: output %d, input %d", output, input);
-
- if (output < sampleCount * 2) {
- output = sampleCount * 2;
- }
- if (input < sampleCount * 2) {
- input = sampleCount * 2;
- }
- ALOGD("adjusted frame count: output %d, input %d", output, input);
-
- // Initialize AudioTrack and AudioRecord.
- AudioTrack track;
- AudioRecord record;
- if (track.set(AUDIO_STREAM_VOICE_CALL, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
- AUDIO_CHANNEL_OUT_MONO, output) != NO_ERROR ||
- record.set(AUDIO_SOURCE_VOICE_COMMUNICATION, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
- AUDIO_CHANNEL_IN_MONO, input) != NO_ERROR) {
- ALOGE("cannot initialize audio device");
- return false;
- }
- ALOGD("latency: output %d, input %d", track.latency(), record.latency());
-
- // Give device socket a reasonable buffer size.
- setsockopt(deviceSocket, SOL_SOCKET, SO_RCVBUF, &output, sizeof(output));
- setsockopt(deviceSocket, SOL_SOCKET, SO_SNDBUF, &output, sizeof(output));
-
- // Drain device socket.
- char c;
- while (recv(deviceSocket, &c, 1, MSG_DONTWAIT) == 1);
-
- // check if platform supports echo cancellation and do not active local echo suppression in
- // this case
- EchoSuppressor *echo = NULL;
- AudioEffect *aec = NULL;
- if (mode == ECHO_SUPPRESSION) {
- if (mGroup->platformHasAec()) {
- aec = new AudioEffect(FX_IID_AEC,
- NULL,
- 0,
- 0,
- 0,
- record.getSessionId(),
- record.getInput());
- status_t status = aec->initCheck();
- if (status == NO_ERROR || status == ALREADY_EXISTS) {
- aec->setEnabled(true);
- } else {
- delete aec;
- aec = NULL;
- }
- }
- // Create local echo suppressor if platform AEC cannot be used.
- if (aec == NULL) {
- echo = new EchoSuppressor(sampleCount,
- (track.latency() + record.latency()) * sampleRate / 1000);
- }
- }
- // Start AudioRecord before AudioTrack. This prevents AudioTrack from being
- // disabled due to buffer underrun while waiting for AudioRecord.
- if (mode != MUTED) {
- record.start();
- int16_t one;
- record.read(&one, sizeof(one));
- }
- track.start();
-
- while (!exitPending()) {
- int16_t output[sampleCount];
- if (recv(deviceSocket, output, sizeof(output), 0) <= 0) {
- memset(output, 0, sizeof(output));
- }
-
- int16_t input[sampleCount];
- int toWrite = sampleCount;
- int toRead = (mode == MUTED) ? 0 : sampleCount;
- int chances = 100;
-
- while (--chances > 0 && (toWrite > 0 || toRead > 0)) {
- if (toWrite > 0) {
- AudioTrack::Buffer buffer;
- buffer.frameCount = toWrite;
-
- status_t status = track.obtainBuffer(&buffer, 1);
- if (status == NO_ERROR) {
- int offset = sampleCount - toWrite;
- memcpy(buffer.i8, &output[offset], buffer.size);
- toWrite -= buffer.frameCount;
- track.releaseBuffer(&buffer);
- } else if (status != TIMED_OUT && status != WOULD_BLOCK) {
- ALOGE("cannot write to AudioTrack");
- goto exit;
- }
- }
-
- if (toRead > 0) {
- AudioRecord::Buffer buffer;
- buffer.frameCount = toRead;
-
- status_t status = record.obtainBuffer(&buffer, 1);
- if (status == NO_ERROR) {
- int offset = sampleCount - toRead;
- memcpy(&input[offset], buffer.i8, buffer.size);
- toRead -= buffer.frameCount;
- record.releaseBuffer(&buffer);
- } else if (status != TIMED_OUT && status != WOULD_BLOCK) {
- ALOGE("cannot read from AudioRecord");
- goto exit;
- }
- }
- }
-
- if (chances <= 0) {
- ALOGW("device loop timeout");
- while (recv(deviceSocket, &c, 1, MSG_DONTWAIT) == 1);
- }
-
- if (mode != MUTED) {
- if (echo != NULL) {
- ALOGV("echo->run()");
- echo->run(output, input);
- }
- send(deviceSocket, input, sizeof(input), MSG_DONTWAIT);
- }
- }
-
-exit:
- delete echo;
- delete aec;
- return true;
-}
-
-//------------------------------------------------------------------------------
-
-static jfieldID gNative;
-static jfieldID gMode;
-
-int add(JNIEnv *env, jobject thiz, jint mode,
- jint socket, jstring jRemoteAddress, jint remotePort,
- jstring jCodecSpec, jint dtmfType)
-{
- AudioCodec *codec = NULL;
- AudioStream *stream = NULL;
- AudioGroup *group = NULL;
-
- // Sanity check.
- sockaddr_storage remote;
- if (parse(env, jRemoteAddress, remotePort, &remote) < 0) {
- // Exception already thrown.
- return 0;
- }
- if (!jCodecSpec) {
- jniThrowNullPointerException(env, "codecSpec");
- return 0;
- }
- const char *codecSpec = env->GetStringUTFChars(jCodecSpec, NULL);
- if (!codecSpec) {
- // Exception already thrown.
- return 0;
- }
- socket = dup(socket);
- if (socket == -1) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "cannot get stream socket");
- return 0;
- }
-
- // Create audio codec.
- int codecType = -1;
- char codecName[16];
- int sampleRate = -1;
- sscanf(codecSpec, "%d %15[^/]%*c%d", &codecType, codecName, &sampleRate);
- codec = newAudioCodec(codecName);
- int sampleCount = (codec ? codec->set(sampleRate, codecSpec) : -1);
- env->ReleaseStringUTFChars(jCodecSpec, codecSpec);
- if (sampleCount <= 0) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "cannot initialize audio codec");
- goto error;
- }
-
- // Create audio stream.
- stream = new AudioStream;
- if (!stream->set(mode, socket, &remote, codec, sampleRate, sampleCount,
- codecType, dtmfType)) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "cannot initialize audio stream");
- goto error;
- }
- socket = -1;
- codec = NULL;
-
- // Create audio group.
- group = (AudioGroup *)env->GetIntField(thiz, gNative);
- if (!group) {
- int mode = env->GetIntField(thiz, gMode);
- group = new AudioGroup;
- if (!group->set(8000, 256) || !group->setMode(mode)) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "cannot initialize audio group");
- goto error;
- }
- }
-
- // Add audio stream into audio group.
- if (!group->add(stream)) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "cannot add audio stream");
- goto error;
- }
-
- // Succeed.
- env->SetIntField(thiz, gNative, (int)group);
- return (int)stream;
-
-error:
- delete group;
- delete stream;
- delete codec;
- close(socket);
- env->SetIntField(thiz, gNative, 0);
- return 0;
-}
-
-void remove(JNIEnv *env, jobject thiz, jint stream)
-{
- AudioGroup *group = (AudioGroup *)env->GetIntField(thiz, gNative);
- if (group) {
- if (!stream || !group->remove((AudioStream *)stream)) {
- delete group;
- env->SetIntField(thiz, gNative, 0);
- }
- }
-}
-
-void setMode(JNIEnv *env, jobject thiz, jint mode)
-{
- AudioGroup *group = (AudioGroup *)env->GetIntField(thiz, gNative);
- if (group && !group->setMode(mode)) {
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- }
-}
-
-void sendDtmf(JNIEnv *env, jobject thiz, jint event)
-{
- AudioGroup *group = (AudioGroup *)env->GetIntField(thiz, gNative);
- if (group && !group->sendDtmf(event)) {
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- }
-}
-
-JNINativeMethod gMethods[] = {
- {"nativeAdd", "(IILjava/lang/String;ILjava/lang/String;I)I", (void *)add},
- {"nativeRemove", "(I)V", (void *)remove},
- {"nativeSetMode", "(I)V", (void *)setMode},
- {"nativeSendDtmf", "(I)V", (void *)sendDtmf},
-};
-
-} // namespace
-
-int registerAudioGroup(JNIEnv *env)
-{
- gRandom = open("/dev/urandom", O_RDONLY);
- if (gRandom == -1) {
- ALOGE("urandom: %s", strerror(errno));
- return -1;
- }
-
- jclass clazz;
- if ((clazz = env->FindClass("android/net/rtp/AudioGroup")) == NULL ||
- (gNative = env->GetFieldID(clazz, "mNative", "I")) == NULL ||
- (gMode = env->GetFieldID(clazz, "mMode", "I")) == NULL ||
- env->RegisterNatives(clazz, gMethods, NELEM(gMethods)) < 0) {
- ALOGE("JNI registration failed");
- return -1;
- }
- return 0;
-}
diff --git a/voip/jni/rtp/EchoSuppressor.cpp b/voip/jni/rtp/EchoSuppressor.cpp
deleted file mode 100644
index e223136..0000000
--- a/voip/jni/rtp/EchoSuppressor.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyrightm (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.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-#include <string.h>
-#include <math.h>
-
-#define LOG_TAG "Echo"
-#include <utils/Log.h>
-
-#include "EchoSuppressor.h"
-
-// It is very difficult to do echo cancellation at this level due to the lack of
-// the timing information of the samples being played and recorded. Therefore,
-// for the first release only echo suppression is implemented.
-
-// The algorithm is derived from the "previous works" summarized in
-// A new class of doubletalk detectors based on cross-correlation,
-// J Benesty, DR Morgan, JH Cho, IEEE Trans. on Speech and Audio Processing.
-// The method proposed in that paper is not used because of its high complexity.
-
-// It is well known that cross-correlation can be computed using convolution,
-// but unfortunately not every mobile processor has a (fast enough) FPU. Thus
-// we use integer arithmetic as much as possible and do lots of bookkeeping.
-// Again, parameters and thresholds are chosen by experiments.
-
-EchoSuppressor::EchoSuppressor(int sampleCount, int tailLength)
-{
- tailLength += sampleCount * 4;
-
- int shift = 0;
- while ((sampleCount >> shift) > 1 && (tailLength >> shift) > 256) {
- ++shift;
- }
-
- mShift = shift + 4;
- mScale = 1 << shift;
- mSampleCount = sampleCount;
- mWindowSize = sampleCount >> shift;
- mTailLength = tailLength >> shift;
- mRecordLength = tailLength * 2 / sampleCount;
- mRecordOffset = 0;
-
- mXs = new uint16_t[mTailLength + mWindowSize];
- memset(mXs, 0, sizeof(*mXs) * (mTailLength + mWindowSize));
- mXSums = new uint32_t[mTailLength];
- memset(mXSums, 0, sizeof(*mXSums) * mTailLength);
- mX2Sums = new uint32_t[mTailLength];
- memset(mX2Sums, 0, sizeof(*mX2Sums) * mTailLength);
- mXRecords = new uint16_t[mRecordLength * mWindowSize];
- memset(mXRecords, 0, sizeof(*mXRecords) * mRecordLength * mWindowSize);
-
- mYSum = 0;
- mY2Sum = 0;
- mYRecords = new uint32_t[mRecordLength];
- memset(mYRecords, 0, sizeof(*mYRecords) * mRecordLength);
- mY2Records = new uint32_t[mRecordLength];
- memset(mY2Records, 0, sizeof(*mY2Records) * mRecordLength);
-
- mXYSums = new uint32_t[mTailLength];
- memset(mXYSums, 0, sizeof(*mXYSums) * mTailLength);
- mXYRecords = new uint32_t[mRecordLength * mTailLength];
- memset(mXYRecords, 0, sizeof(*mXYRecords) * mRecordLength * mTailLength);
-
- mLastX = 0;
- mLastY = 0;
- mWeight = 1.0f / (mRecordLength * mWindowSize);
-}
-
-EchoSuppressor::~EchoSuppressor()
-{
- delete [] mXs;
- delete [] mXSums;
- delete [] mX2Sums;
- delete [] mXRecords;
- delete [] mYRecords;
- delete [] mY2Records;
- delete [] mXYSums;
- delete [] mXYRecords;
-}
-
-void EchoSuppressor::run(int16_t *playbacked, int16_t *recorded)
-{
- // Update Xs.
- for (int i = mTailLength - 1; i >= 0; --i) {
- mXs[i + mWindowSize] = mXs[i];
- }
- for (int i = mWindowSize - 1, j = 0; i >= 0; --i, j += mScale) {
- uint32_t sum = 0;
- for (int k = 0; k < mScale; ++k) {
- int32_t x = playbacked[j + k] << 15;
- mLastX += x;
- sum += ((mLastX >= 0) ? mLastX : -mLastX) >> 15;
- mLastX -= (mLastX >> 10) + x;
- }
- mXs[i] = sum >> mShift;
- }
-
- // Update XSums, X2Sums, and XRecords.
- for (int i = mTailLength - mWindowSize - 1; i >= 0; --i) {
- mXSums[i + mWindowSize] = mXSums[i];
- mX2Sums[i + mWindowSize] = mX2Sums[i];
- }
- uint16_t *xRecords = &mXRecords[mRecordOffset * mWindowSize];
- for (int i = mWindowSize - 1; i >= 0; --i) {
- uint16_t x = mXs[i];
- mXSums[i] = mXSums[i + 1] + x - xRecords[i];
- mX2Sums[i] = mX2Sums[i + 1] + x * x - xRecords[i] * xRecords[i];
- xRecords[i] = x;
- }
-
- // Compute Ys.
- uint16_t ys[mWindowSize];
- for (int i = mWindowSize - 1, j = 0; i >= 0; --i, j += mScale) {
- uint32_t sum = 0;
- for (int k = 0; k < mScale; ++k) {
- int32_t y = recorded[j + k] << 15;
- mLastY += y;
- sum += ((mLastY >= 0) ? mLastY : -mLastY) >> 15;
- mLastY -= (mLastY >> 10) + y;
- }
- ys[i] = sum >> mShift;
- }
-
- // Update YSum, Y2Sum, YRecords, and Y2Records.
- uint32_t ySum = 0;
- uint32_t y2Sum = 0;
- for (int i = mWindowSize - 1; i >= 0; --i) {
- ySum += ys[i];
- y2Sum += ys[i] * ys[i];
- }
- mYSum += ySum - mYRecords[mRecordOffset];
- mY2Sum += y2Sum - mY2Records[mRecordOffset];
- mYRecords[mRecordOffset] = ySum;
- mY2Records[mRecordOffset] = y2Sum;
-
- // Update XYSums and XYRecords.
- uint32_t *xyRecords = &mXYRecords[mRecordOffset * mTailLength];
- for (int i = mTailLength - 1; i >= 0; --i) {
- uint32_t xySum = 0;
- for (int j = mWindowSize - 1; j >= 0; --j) {
- xySum += mXs[i + j] * ys[j];
- }
- mXYSums[i] += xySum - xyRecords[i];
- xyRecords[i] = xySum;
- }
-
- // Compute correlations.
- int latency = 0;
- float corr2 = 0.0f;
- float varX = 0.0f;
- float varY = mY2Sum - mWeight * mYSum * mYSum;
- for (int i = mTailLength - 1; i >= 0; --i) {
- float cov = mXYSums[i] - mWeight * mXSums[i] * mYSum;
- if (cov > 0.0f) {
- float varXi = mX2Sums[i] - mWeight * mXSums[i] * mXSums[i];
- float corr2i = cov * cov / (varXi * varY + 1);
- if (corr2i > corr2) {
- varX = varXi;
- corr2 = corr2i;
- latency = i;
- }
- }
- }
- //ALOGI("corr^2 %.5f, var %8.0f %8.0f, latency %d", corr2, varX, varY,
- // latency * mScale);
-
- // Do echo suppression.
- if (corr2 > 0.1f && varX > 10000.0f) {
- int factor = (corr2 > 1.0f) ? 0 : (1.0f - sqrtf(corr2)) * 4096;
- for (int i = 0; i < mSampleCount; ++i) {
- recorded[i] = recorded[i] * factor >> 16;
- }
- }
-
- // Increase RecordOffset.
- ++mRecordOffset;
- if (mRecordOffset == mRecordLength) {
- mRecordOffset = 0;
- }
-}
diff --git a/voip/jni/rtp/EchoSuppressor.h b/voip/jni/rtp/EchoSuppressor.h
deleted file mode 100644
index 2f3b593..0000000
--- a/voip/jni/rtp/EchoSuppressor.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyrightm (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.
- */
-
-#ifndef __ECHO_SUPPRESSOR_H__
-#define __ECHO_SUPPRESSOR_H__
-
-#include <stdint.h>
-
-class EchoSuppressor
-{
-public:
- // The sampleCount must be power of 2.
- EchoSuppressor(int sampleCount, int tailLength);
- ~EchoSuppressor();
- void run(int16_t *playbacked, int16_t *recorded);
-
-private:
- int mShift;
- int mScale;
- int mSampleCount;
- int mWindowSize;
- int mTailLength;
- int mRecordLength;
- int mRecordOffset;
-
- uint16_t *mXs;
- uint32_t *mXSums;
- uint32_t *mX2Sums;
- uint16_t *mXRecords;
-
- uint32_t mYSum;
- uint32_t mY2Sum;
- uint32_t *mYRecords;
- uint32_t *mY2Records;
-
- uint32_t *mXYSums;
- uint32_t *mXYRecords;
-
- int32_t mLastX;
- int32_t mLastY;
-
- float mWeight;
-};
-
-#endif
diff --git a/voip/jni/rtp/G711Codec.cpp b/voip/jni/rtp/G711Codec.cpp
deleted file mode 100644
index ef54863..0000000
--- a/voip/jni/rtp/G711Codec.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyrightm (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.
- */
-
-#include "AudioCodec.h"
-
-namespace {
-
-const int8_t gExponents[128] = {
- 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-};
-
-//------------------------------------------------------------------------------
-
-class UlawCodec : public AudioCodec
-{
-public:
- int set(int sampleRate, const char *fmtp) {
- mSampleCount = sampleRate / 50;
- return mSampleCount;
- }
- int encode(void *payload, int16_t *samples);
- int decode(int16_t *samples, int count, void *payload, int length);
-private:
- int mSampleCount;
-};
-
-int UlawCodec::encode(void *payload, int16_t *samples)
-{
- int8_t *ulaws = (int8_t *)payload;
- for (int i = 0; i < mSampleCount; ++i) {
- int sample = samples[i];
- int sign = (sample >> 8) & 0x80;
- if (sample < 0) {
- sample = -sample;
- }
- sample += 132;
- if (sample > 32767) {
- sample = 32767;
- }
- int exponent = gExponents[sample >> 8];
- int mantissa = (sample >> (exponent + 3)) & 0x0F;
- ulaws[i] = ~(sign | (exponent << 4) | mantissa);
- }
- return mSampleCount;
-}
-
-int UlawCodec::decode(int16_t *samples, int count, void *payload, int length)
-{
- int8_t *ulaws = (int8_t *)payload;
- if (length > count) {
- length = count;
- }
- for (int i = 0; i < length; ++i) {
- int ulaw = ~ulaws[i];
- int exponent = (ulaw >> 4) & 0x07;
- int mantissa = ulaw & 0x0F;
- int sample = (((mantissa << 3) + 132) << exponent) - 132;
- samples[i] = (ulaw < 0 ? -sample : sample);
- }
- return length;
-}
-
-//------------------------------------------------------------------------------
-
-class AlawCodec : public AudioCodec
-{
-public:
- int set(int sampleRate, const char *fmtp) {
- mSampleCount = sampleRate / 50;
- return mSampleCount;
- }
- int encode(void *payload, int16_t *samples);
- int decode(int16_t *samples, int count, void *payload, int length);
-private:
- int mSampleCount;
-};
-
-int AlawCodec::encode(void *payload, int16_t *samples)
-{
- int8_t *alaws = (int8_t *)payload;
- for (int i = 0; i < mSampleCount; ++i) {
- int sample = samples[i];
- int sign = (sample >> 8) & 0x80;
- if (sample < 0) {
- sample = -sample;
- }
- if (sample > 32767) {
- sample = 32767;
- }
- int exponent = gExponents[sample >> 8];
- int mantissa = (sample >> (exponent == 0 ? 4 : exponent + 3)) & 0x0F;
- alaws[i] = (sign | (exponent << 4) | mantissa) ^ 0xD5;
- }
- return mSampleCount;
-}
-
-int AlawCodec::decode(int16_t *samples, int count, void *payload, int length)
-{
- int8_t *alaws = (int8_t *)payload;
- if (length > count) {
- length = count;
- }
- for (int i = 0; i < length; ++i) {
- int alaw = alaws[i] ^ 0x55;
- int exponent = (alaw >> 4) & 0x07;
- int mantissa = alaw & 0x0F;
- int sample = (exponent == 0 ? (mantissa << 4) + 8 :
- ((mantissa << 3) + 132) << exponent);
- samples[i] = (alaw < 0 ? sample : -sample);
- }
- return length;
-}
-
-} // namespace
-
-AudioCodec *newUlawCodec()
-{
- return new UlawCodec;
-}
-
-AudioCodec *newAlawCodec()
-{
- return new AlawCodec;
-}
diff --git a/voip/jni/rtp/GsmCodec.cpp b/voip/jni/rtp/GsmCodec.cpp
deleted file mode 100644
index 61dfdc9..0000000
--- a/voip/jni/rtp/GsmCodec.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyrightm (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.
- */
-
-#include "AudioCodec.h"
-
-extern "C" {
-#include "gsm.h"
-}
-
-namespace {
-
-class GsmCodec : public AudioCodec
-{
-public:
- GsmCodec() {
- mEncode = gsm_create();
- mDecode = gsm_create();
- }
-
- ~GsmCodec() {
- if (mEncode) {
- gsm_destroy(mEncode);
- }
- if (mDecode) {
- gsm_destroy(mDecode);
- }
- }
-
- int set(int sampleRate, const char *fmtp) {
- return (sampleRate == 8000 && mEncode && mDecode) ? 160 : -1;
- }
-
- int encode(void *payload, int16_t *samples);
- int decode(int16_t *samples, int count, void *payload, int length);
-
-private:
- gsm mEncode;
- gsm mDecode;
-};
-
-int GsmCodec::encode(void *payload, int16_t *samples)
-{
- gsm_encode(mEncode, samples, (unsigned char *)payload);
- return 33;
-}
-
-int GsmCodec::decode(int16_t *samples, int count, void *payload, int length)
-{
- unsigned char *bytes = (unsigned char *)payload;
- int n = 0;
- while (n + 160 <= count && length >= 33 &&
- gsm_decode(mDecode, bytes, &samples[n]) == 0) {
- n += 160;
- length -= 33;
- bytes += 33;
- }
- return n;
-}
-
-} // namespace
-
-AudioCodec *newGsmCodec()
-{
- return new GsmCodec;
-}
diff --git a/voip/jni/rtp/RtpStream.cpp b/voip/jni/rtp/RtpStream.cpp
deleted file mode 100644
index bfe8e24..0000000
--- a/voip/jni/rtp/RtpStream.cpp
+++ /dev/null
@@ -1,113 +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.
- */
-
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-
-#define LOG_TAG "RtpStream"
-#include <utils/Log.h>
-
-#include "jni.h"
-#include "JNIHelp.h"
-
-extern int parse(JNIEnv *env, jstring jAddress, int port, sockaddr_storage *ss);
-
-namespace {
-
-jfieldID gSocket;
-
-jint create(JNIEnv *env, jobject thiz, jstring jAddress)
-{
- env->SetIntField(thiz, gSocket, -1);
-
- sockaddr_storage ss;
- if (parse(env, jAddress, 0, &ss) < 0) {
- // Exception already thrown.
- return -1;
- }
-
- int socket = ::socket(ss.ss_family, SOCK_DGRAM, 0);
- socklen_t len = sizeof(ss);
- if (socket == -1 || bind(socket, (sockaddr *)&ss, sizeof(ss)) != 0 ||
- getsockname(socket, (sockaddr *)&ss, &len) != 0) {
- jniThrowException(env, "java/net/SocketException", strerror(errno));
- ::close(socket);
- return -1;
- }
-
- uint16_t *p = (ss.ss_family == AF_INET) ?
- &((sockaddr_in *)&ss)->sin_port : &((sockaddr_in6 *)&ss)->sin6_port;
- uint16_t port = ntohs(*p);
- if ((port & 1) == 0) {
- env->SetIntField(thiz, gSocket, socket);
- return port;
- }
- ::close(socket);
-
- socket = ::socket(ss.ss_family, SOCK_DGRAM, 0);
- if (socket != -1) {
- uint16_t delta = port << 1;
- ++port;
-
- for (int i = 0; i < 1000; ++i) {
- do {
- port += delta;
- } while (port < 1024);
- *p = htons(port);
-
- if (bind(socket, (sockaddr *)&ss, sizeof(ss)) == 0) {
- env->SetIntField(thiz, gSocket, socket);
- return port;
- }
- }
- }
-
- jniThrowException(env, "java/net/SocketException", strerror(errno));
- ::close(socket);
- return -1;
-}
-
-void close(JNIEnv *env, jobject thiz)
-{
- int socket = env->GetIntField(thiz, gSocket);
- ::close(socket);
- env->SetIntField(thiz, gSocket, -1);
-}
-
-JNINativeMethod gMethods[] = {
- {"create", "(Ljava/lang/String;)I", (void *)create},
- {"close", "()V", (void *)close},
-};
-
-} // namespace
-
-int registerRtpStream(JNIEnv *env)
-{
- jclass clazz;
- if ((clazz = env->FindClass("android/net/rtp/RtpStream")) == NULL ||
- (gSocket = env->GetFieldID(clazz, "mSocket", "I")) == NULL ||
- env->RegisterNatives(clazz, gMethods, NELEM(gMethods)) < 0) {
- ALOGE("JNI registration failed");
- return -1;
- }
- return 0;
-}
diff --git a/voip/jni/rtp/rtp_jni.cpp b/voip/jni/rtp/rtp_jni.cpp
deleted file mode 100644
index 9f4bff9..0000000
--- a/voip/jni/rtp/rtp_jni.cpp
+++ /dev/null
@@ -1,32 +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.
- */
-
-#include <stdio.h>
-
-#include "jni.h"
-
-extern int registerRtpStream(JNIEnv *env);
-extern int registerAudioGroup(JNIEnv *env);
-
-__attribute__((visibility("default"))) jint JNI_OnLoad(JavaVM *vm, void *unused)
-{
- JNIEnv *env = NULL;
- if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK ||
- registerRtpStream(env) < 0 || registerAudioGroup(env) < 0) {
- return -1;
- }
- return JNI_VERSION_1_4;
-}
diff --git a/voip/jni/rtp/util.cpp b/voip/jni/rtp/util.cpp
deleted file mode 100644
index 1d702fc..0000000
--- a/voip/jni/rtp/util.cpp
+++ /dev/null
@@ -1,61 +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.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-
-#include "jni.h"
-#include "JNIHelp.h"
-
-int parse(JNIEnv *env, jstring jAddress, int port, sockaddr_storage *ss)
-{
- if (!jAddress) {
- jniThrowNullPointerException(env, "address");
- return -1;
- }
- if (port < 0 || port > 65535) {
- jniThrowException(env, "java/lang/IllegalArgumentException", "port");
- return -1;
- }
- const char *address = env->GetStringUTFChars(jAddress, NULL);
- if (!address) {
- // Exception already thrown.
- return -1;
- }
- memset(ss, 0, sizeof(*ss));
-
- sockaddr_in *sin = (sockaddr_in *)ss;
- if (inet_pton(AF_INET, address, &(sin->sin_addr)) > 0) {
- sin->sin_family = AF_INET;
- sin->sin_port = htons(port);
- env->ReleaseStringUTFChars(jAddress, address);
- return 0;
- }
-
- sockaddr_in6 *sin6 = (sockaddr_in6 *)ss;
- if (inet_pton(AF_INET6, address, &(sin6->sin6_addr)) > 0) {
- sin6->sin6_family = AF_INET6;
- sin6->sin6_port = htons(port);
- env->ReleaseStringUTFChars(jAddress, address);
- return 0;
- }
-
- env->ReleaseStringUTFChars(jAddress, address);
- jniThrowException(env, "java/lang/IllegalArgumentException", "address");
- return -1;
-}