RTP: refactor out the network part from AudioStream to RtpStream.

Change-Id: Ie0922a46be3202f2a19fb3b90fd01562ea2514b3
diff --git a/src/android/net/rtp/AudioStream.java b/src/android/net/rtp/AudioStream.java
new file mode 100644
index 0000000..c1da7ba
--- /dev/null
+++ b/src/android/net/rtp/AudioStream.java
@@ -0,0 +1,135 @@
+/*
+ * 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;
+
+/**
+ * AudioStream represents a RTP stream carrying audio payloads.
+ */
+public class AudioStream extends RtpStream {
+    private AudioCodec mCodec;
+    private int mCodecType = -1;
+    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 already joined an {@link AudioGroup}.
+     */
+    @Override
+    public final boolean isBusy() {
+        return mGroup != null;
+    }
+
+    /**
+     * Returns the joined {@link AudioGroup}.
+     */
+    public AudioGroup getAudioGroup() {
+        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) {
+        if (mGroup == group) {
+            return;
+        }
+        if (mGroup != null) {
+            mGroup.remove(this);
+            mGroup = null;
+        }
+        if (group != null) {
+            group.add(this, mCodec, mCodecType, mDtmfType);
+            mGroup = group;
+        }
+    }
+
+    /**
+     * Sets the {@link AudioCodec} and its RTP payload type. According to RFC
+     * 3551, the type must be in the range of 0 and 127, where 96 and above are
+     * dynamic types. For codecs with static mappings (non-negative
+     * {@link AudioCodec#defaultType}), assigning a different non-dynamic type
+     * is disallowed.
+     *
+     * @param codec The AudioCodec to be used.
+     * @param type The RTP payload type.
+     * @throws IllegalArgumentException if the type is invalid or used by DTMF.
+     * @throws IllegalStateException if the stream is busy.
+     */
+    public void setCodec(AudioCodec codec, int type) {
+        if (isBusy()) {
+            throw new IllegalStateException("Busy");
+        }
+        if (type < 0 || type > 127 || (type != codec.defaultType && type < 96)) {
+            throw new IllegalArgumentException("Invalid type");
+        }
+        if (type == mDtmfType) {
+            throw new IllegalArgumentException("The type is used by DTMF");
+        }
+        mCodec = codec;
+        mCodecType = type;
+    }
+
+    /**
+     * 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 value. 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 (type == mCodecType) {
+                throw new IllegalArgumentException("The type is used by codec");
+            }
+        }
+        mDtmfType = type;
+    }
+}
diff --git a/src/android/net/rtp/RtpStream.java b/src/android/net/rtp/RtpStream.java
new file mode 100644
index 0000000..b542ca7
--- /dev/null
+++ b/src/android/net/rtp/RtpStream.java
@@ -0,0 +1,172 @@
+/*
+ * 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 a base class of media streams running over
+ * Real-time Transport Protocol (RTP).
+ */
+public class RtpStream {
+    public static final int MODE_NORMAL = 0;
+    public static final int MODE_SEND_ONLY = 1;
+    public static final int MODE_RECEIVE_ONLY = 2;
+
+    private final InetAddress mLocalAddress;
+    private final int mLocalPort;
+
+    private InetAddress mRemoteAddress;
+    private int mRemotePort = -1;
+    private int mMode = MODE_NORMAL;
+
+    private int mNative;
+    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. This method is intended to be
+     * overridden by subclasses.
+     */
+    public boolean isBusy() {
+        return false;
+    }
+
+    /**
+     * Returns the current mode. The initial mode is {@link #MODE_NORMAL}.
+     */
+    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 != MODE_NORMAL && mode != MODE_SEND_ONLY && mode != MODE_RECEIVE_ONLY) {
+            throw new IllegalArgumentException("Invalid mode");
+        }
+        mMode = mode;
+    }
+
+    /**
+     * Associates with a remote host.
+     *
+     * @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;
+    }
+
+    synchronized native int dup();
+
+    /**
+     * Releases allocated resources. The stream becomes inoperable after calling
+     * this method.
+     *
+     * @throws IllegalStateException if the stream is busy.
+     * @see #isBusy()
+     */
+    public void release() {
+        if (isBusy()) {
+            throw new IllegalStateException("Busy");
+        }
+        close();
+    }
+
+    private synchronized native void close();
+
+    @Override
+    protected void finalize() throws Throwable {
+        close();
+        super.finalize();
+    }
+}