1e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh/*
2e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * Copyright (C) 2010 The Android Open Source Project
3e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh *
4e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * Licensed under the Apache License, Version 2.0 (the "License");
5e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * you may not use this file except in compliance with the License.
6e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * You may obtain a copy of the License at
7e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh *
8e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh *      http://www.apache.org/licenses/LICENSE-2.0
9e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh *
10e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * Unless required by applicable law or agreed to in writing, software
11e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * distributed under the License is distributed on an "AS IS" BASIS,
12e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * See the License for the specific language governing permissions and
14e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * limitations under the License.
15e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh */
16e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
17e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yehpackage android.net.sip;
18e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
19e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yehimport java.util.ArrayList;
20e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yehimport java.util.Arrays;
217314532349e402315af9b8f664432dd18292421fJohan Redestigimport java.util.Locale;
22e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
23e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh/**
24e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * An object used to manipulate messages of Session Description Protocol (SDP).
25e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * It is mainly designed for the uses of Session Initiation Protocol (SIP).
26e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * Therefore, it only handles connection addresses ("c="), bandwidth limits,
27e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * ("b="), encryption keys ("k="), and attribute fields ("a="). Currently this
28e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * implementation does not support multicast sessions.
29e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh *
30e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * <p>Here is an example code to create a session description.</p>
31e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * <pre>
32e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * SimpleSessionDescription description = new SimpleSessionDescription(
33e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh *     System.currentTimeMillis(), "1.2.3.4");
34e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * Media media = description.newMedia("audio", 56789, 1, "RTP/AVP");
35e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * media.setRtpPayload(0, "PCMU/8000", null);
36e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * media.setRtpPayload(8, "PCMA/8000", null);
37e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * media.setRtpPayload(127, "telephone-event/8000", "0-15");
38e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * media.setAttribute("sendrecv", "");
39e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * </pre>
40e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * <p>Invoking <code>description.encode()</code> will produce a result like the
41e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * one below.</p>
42e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * <pre>
43e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * v=0
44e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * o=- 1284970442706 1284970442709 IN IP4 1.2.3.4
45e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * s=-
46e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * c=IN IP4 1.2.3.4
47e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * t=0 0
48e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * m=audio 56789 RTP/AVP 0 8 127
49e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * a=rtpmap:0 PCMU/8000
50e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * a=rtpmap:8 PCMA/8000
51e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * a=rtpmap:127 telephone-event/8000
52e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * a=fmtp:127 0-15
53e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * a=sendrecv
54e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * </pre>
55e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh * @hide
56e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh */
57e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yehpublic class SimpleSessionDescription {
58e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    private final Fields mFields = new Fields("voscbtka");
59e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    private final ArrayList<Media> mMedia = new ArrayList<Media>();
60e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
61e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    /**
62e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * Creates a minimal session description from the given session ID and
63e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * unicast address. The address is used in the origin field ("o=") and the
64e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * connection field ("c="). See {@link SimpleSessionDescription} for an
65e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * example of its usage.
66e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     */
67e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    public SimpleSessionDescription(long sessionId, String address) {
68e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        address = (address.indexOf(':') < 0 ? "IN IP4 " : "IN IP6 ") + address;
69e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        mFields.parse("v=0");
707314532349e402315af9b8f664432dd18292421fJohan Redestig        mFields.parse(String.format(Locale.US, "o=- %d %d %s", sessionId,
71e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                System.currentTimeMillis(), address));
72e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        mFields.parse("s=-");
73e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        mFields.parse("t=0 0");
74e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        mFields.parse("c=" + address);
75e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    }
76e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
77e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    /**
78e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * Creates a session description from the given message.
79e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     *
80e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * @throws IllegalArgumentException if message is invalid.
81e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     */
82e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    public SimpleSessionDescription(String message) {
83e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        String[] lines = message.trim().replaceAll(" +", " ").split("[\r\n]+");
84e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        Fields fields = mFields;
85e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
86e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        for (String line : lines) {
87e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            try {
88e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                if (line.charAt(1) != '=') {
89e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    throw new IllegalArgumentException();
90e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                }
91e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                if (line.charAt(0) == 'm') {
92e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    String[] parts = line.substring(2).split(" ", 4);
93e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    String[] ports = parts[1].split("/", 2);
94e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    Media media = newMedia(parts[0], Integer.parseInt(ports[0]),
95e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                            (ports.length < 2) ? 1 : Integer.parseInt(ports[1]),
96e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                            parts[2]);
97e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    for (String format : parts[3].split(" ")) {
98e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                        media.setFormat(format, null);
99e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    }
100e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    fields = media;
101e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                } else {
102e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    fields.parse(line);
103e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                }
104e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            } catch (Exception e) {
105e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                throw new IllegalArgumentException("Invalid SDP: " + line);
106e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            }
107e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
108e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    }
109e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
110e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    /**
111e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * Creates a new media description in this session description.
112e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     *
113e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * @param type The media type, e.g. {@code "audio"}.
114e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * @param port The first transport port used by this media.
115e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * @param portCount The number of contiguous ports used by this media.
116e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * @param protocol The transport protocol, e.g. {@code "RTP/AVP"}.
117e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     */
118e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    public Media newMedia(String type, int port, int portCount,
119e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            String protocol) {
120e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        Media media = new Media(type, port, portCount, protocol);
121e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        mMedia.add(media);
122e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        return media;
123e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    }
124e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
125e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    /**
126e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * Returns all the media descriptions in this session description.
127e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     */
128e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    public Media[] getMedia() {
129e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        return mMedia.toArray(new Media[mMedia.size()]);
130e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    }
131e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
132e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    /**
133e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * Encodes the session description and all its media descriptions in a
134e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * string. Note that the result might be incomplete if a required field
135e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * has never been added before.
136e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     */
137e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    public String encode() {
138e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        StringBuilder buffer = new StringBuilder();
139e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        mFields.write(buffer);
140e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        for (Media media : mMedia) {
141e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            media.write(buffer);
142e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
143e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        return buffer.toString();
144e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    }
145e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
146e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    /**
147e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * Returns the connection address or {@code null} if it is not present.
148e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     */
149e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    public String getAddress() {
150e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        return mFields.getAddress();
151e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    }
152e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
153e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    /**
154e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * Sets the connection address. The field will be removed if the address
155e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * is {@code null}.
156e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     */
157e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    public void setAddress(String address) {
158e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        mFields.setAddress(address);
159e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    }
160e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
161e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    /**
162e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * Returns the encryption method or {@code null} if it is not present.
163e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     */
164e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    public String getEncryptionMethod() {
165e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        return mFields.getEncryptionMethod();
166e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    }
167e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
168e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    /**
169e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * Returns the encryption key or {@code null} if it is not present.
170e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     */
171e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    public String getEncryptionKey() {
172e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        return mFields.getEncryptionKey();
173e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    }
174e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
175e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    /**
176e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * Sets the encryption method and the encryption key. The field will be
177e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * removed if the method is {@code null}.
178e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     */
179e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    public void setEncryption(String method, String key) {
180e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        mFields.setEncryption(method, key);
181e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    }
182e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
183e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    /**
184e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * Returns the types of the bandwidth limits.
185e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     */
186e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    public String[] getBandwidthTypes() {
187e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        return mFields.getBandwidthTypes();
188e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    }
189e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
190e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    /**
191e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * Returns the bandwidth limit of the given type or {@code -1} if it is not
192e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * present.
193e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     */
194e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    public int getBandwidth(String type) {
195e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        return mFields.getBandwidth(type);
196e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    }
197e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
198e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    /**
199e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * Sets the bandwith limit for the given type. The field will be removed if
200e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * the value is negative.
201e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     */
202e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    public void setBandwidth(String type, int value) {
203e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        mFields.setBandwidth(type, value);
204e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    }
205e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
206e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    /**
207e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * Returns the names of all the attributes.
208e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     */
209e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    public String[] getAttributeNames() {
210e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        return mFields.getAttributeNames();
211e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    }
212e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
213e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    /**
214e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * Returns the attribute of the given name or {@code null} if it is not
215e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * present.
216e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     */
217e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    public String getAttribute(String name) {
218e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        return mFields.getAttribute(name);
219e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    }
220e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
221e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    /**
222e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * Sets the attribute for the given name. The field will be removed if
223e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * the value is {@code null}. To set a binary attribute, use an empty
224e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * string as the value.
225e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     */
226e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    public void setAttribute(String name, String value) {
227e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        mFields.setAttribute(name, value);
228e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    }
229e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
230e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    /**
231e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * This class represents a media description of a session description. It
232e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * can only be created by {@link SimpleSessionDescription#newMedia}. Since
233e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * the syntax is more restricted for RTP based protocols, two sets of access
234e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * methods are implemented. See {@link SimpleSessionDescription} for an
235e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * example of its usage.
236e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     */
237e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    public static class Media extends Fields {
238e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        private final String mType;
239e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        private final int mPort;
240e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        private final int mPortCount;
241e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        private final String mProtocol;
242e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        private ArrayList<String> mFormats = new ArrayList<String>();
243e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
244e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        private Media(String type, int port, int portCount, String protocol) {
245e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            super("icbka");
246e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            mType = type;
247e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            mPort = port;
248e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            mPortCount = portCount;
249e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            mProtocol = protocol;
250e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
251e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
252e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
253e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Returns the media type.
254e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
255e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public String getType() {
256e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            return mType;
257e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
258e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
259e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
260e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Returns the first transport port used by this media.
261e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
262e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public int getPort() {
263e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            return mPort;
264e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
265e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
266e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
267e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Returns the number of contiguous ports used by this media.
268e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
269e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public int getPortCount() {
270e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            return mPortCount;
271e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
272e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
273e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
274e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Returns the transport protocol.
275e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
276e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public String getProtocol() {
277e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            return mProtocol;
278e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
279e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
280e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
281e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Returns the media formats.
282e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
283e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public String[] getFormats() {
284e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            return mFormats.toArray(new String[mFormats.size()]);
285e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
286e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
287e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
288e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Returns the {@code fmtp} attribute of the given format or
289e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * {@code null} if it is not present.
290e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
291e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public String getFmtp(String format) {
292e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            return super.get("a=fmtp:" + format, ' ');
293e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
294e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
295e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
296e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Sets a format and its {@code fmtp} attribute. If the attribute is
297e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * {@code null}, the corresponding field will be removed.
298e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
299e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public void setFormat(String format, String fmtp) {
300e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            mFormats.remove(format);
301e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            mFormats.add(format);
302e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            super.set("a=rtpmap:" + format, ' ', null);
303e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            super.set("a=fmtp:" + format, ' ', fmtp);
304e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
305e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
306e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
307e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Removes a format and its {@code fmtp} attribute.
308e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
309e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public void removeFormat(String format) {
310e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            mFormats.remove(format);
311e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            super.set("a=rtpmap:" + format, ' ', null);
312e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            super.set("a=fmtp:" + format, ' ', null);
313e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
314e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
315e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
316e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Returns the RTP payload types.
317e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
318e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public int[] getRtpPayloadTypes() {
319e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            int[] types = new int[mFormats.size()];
320e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            int length = 0;
321e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            for (String format : mFormats) {
322e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                try {
323e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    types[length] = Integer.parseInt(format);
324e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    ++length;
325e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                } catch (NumberFormatException e) { }
326e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            }
327e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            return Arrays.copyOf(types, length);
328e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
329e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
330e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
331e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Returns the {@code rtpmap} attribute of the given RTP payload type
332e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * or {@code null} if it is not present.
333e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
334e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public String getRtpmap(int type) {
335e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            return super.get("a=rtpmap:" + type, ' ');
336e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
337e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
338e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
339e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Returns the {@code fmtp} attribute of the given RTP payload type or
340e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * {@code null} if it is not present.
341e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
342e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public String getFmtp(int type) {
343e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            return super.get("a=fmtp:" + type, ' ');
344e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
345e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
346e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
3470b7d6de1559a4a78af76ab501e0a15afc396c2b9repo sync         * Sets a RTP payload type and its {@code rtpmap} and {@code fmtp}
348e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * attributes. If any of the attributes is {@code null}, the
349e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * corresponding field will be removed. See
350e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * {@link SimpleSessionDescription} for an example of its usage.
351e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
352e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public void setRtpPayload(int type, String rtpmap, String fmtp) {
353e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            String format = String.valueOf(type);
354e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            mFormats.remove(format);
355e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            mFormats.add(format);
356e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            super.set("a=rtpmap:" + format, ' ', rtpmap);
357e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            super.set("a=fmtp:" + format, ' ', fmtp);
358e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
359e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
360e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
361e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Removes a RTP payload and its {@code rtpmap} and {@code fmtp}
362e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * attributes.
363e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
364e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public void removeRtpPayload(int type) {
365e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            removeFormat(String.valueOf(type));
366e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
367e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
368e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        private void write(StringBuilder buffer) {
369e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            buffer.append("m=").append(mType).append(' ').append(mPort);
370e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            if (mPortCount != 1) {
371e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                buffer.append('/').append(mPortCount);
372e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            }
373e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            buffer.append(' ').append(mProtocol);
374e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            for (String format : mFormats) {
375e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                buffer.append(' ').append(format);
376e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            }
377e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            buffer.append("\r\n");
378e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            super.write(buffer);
379e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
380e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    }
381e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
382e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    /**
383e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * This class acts as a set of fields, and the size of the set is expected
384e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * to be small. Therefore, it uses a simple list instead of maps. Each field
385e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * has three parts: a key, a delimiter, and a value. Delimiters are special
386e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * because they are not included in binary attributes. As a result, the
387e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * private methods, which are the building blocks of this class, all take
388e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     * the delimiter as an argument.
389e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh     */
390e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    private static class Fields {
391e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        private final String mOrder;
392e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        private final ArrayList<String> mLines = new ArrayList<String>();
393e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
394e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        Fields(String order) {
395e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            mOrder = order;
396e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
397e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
398e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
399e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Returns the connection address or {@code null} if it is not present.
400e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
401e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public String getAddress() {
402e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            String address = get("c", '=');
403e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            if (address == null) {
404e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                return null;
405e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            }
406e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            String[] parts = address.split(" ");
407e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            if (parts.length != 3) {
408e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                return null;
409e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            }
410e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            int slash = parts[2].indexOf('/');
411e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            return (slash < 0) ? parts[2] : parts[2].substring(0, slash);
412e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
413e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
414e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
415e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Sets the connection address. The field will be removed if the address
416e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * is {@code null}.
417e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
418e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public void setAddress(String address) {
419e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            if (address != null) {
420e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                address = (address.indexOf(':') < 0 ? "IN IP4 " : "IN IP6 ") +
421e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                        address;
422e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            }
423e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            set("c", '=', address);
424e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
425e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
426e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
427e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Returns the encryption method or {@code null} if it is not present.
428e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
429e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public String getEncryptionMethod() {
430e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            String encryption = get("k", '=');
431e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            if (encryption == null) {
432e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                return null;
433e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            }
434e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            int colon = encryption.indexOf(':');
435e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            return (colon == -1) ? encryption : encryption.substring(0, colon);
436e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
437e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
438e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
439e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Returns the encryption key or {@code null} if it is not present.
440e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
441e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public String getEncryptionKey() {
442e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            String encryption = get("k", '=');
443e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            if (encryption == null) {
444e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                return null;
445e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            }
446e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            int colon = encryption.indexOf(':');
447e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            return (colon == -1) ? null : encryption.substring(0, colon + 1);
448e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
449e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
450e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
451e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Sets the encryption method and the encryption key. The field will be
452e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * removed if the method is {@code null}.
453e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
454e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public void setEncryption(String method, String key) {
455e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            set("k", '=', (method == null || key == null) ?
456e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    method : method + ':' + key);
457e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
458e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
459e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
460e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Returns the types of the bandwidth limits.
461e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
462e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public String[] getBandwidthTypes() {
463e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            return cut("b=", ':');
464e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
465e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
466e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
467e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Returns the bandwidth limit of the given type or {@code -1} if it is
468e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * not present.
469e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
470e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public int getBandwidth(String type) {
471e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            String value = get("b=" + type, ':');
472e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            if (value != null) {
473e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                try {
474e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    return Integer.parseInt(value);
475e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                } catch (NumberFormatException e) { }
476e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                setBandwidth(type, -1);
477e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            }
478e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            return -1;
479e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
480e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
481e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
482e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Sets the bandwith limit for the given type. The field will be removed
483e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * if the value is negative.
484e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
485e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public void setBandwidth(String type, int value) {
486e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            set("b=" + type, ':', (value < 0) ? null : String.valueOf(value));
487e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
488e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
489e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
490e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Returns the names of all the attributes.
491e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
492e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public String[] getAttributeNames() {
493e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            return cut("a=", ':');
494e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
495e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
496e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
497e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Returns the attribute of the given name or {@code null} if it is not
498e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * present.
499e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
500e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public String getAttribute(String name) {
501e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            return get("a=" + name, ':');
502e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
503e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
504e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
505e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Sets the attribute for the given name. The field will be removed if
506e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * the value is {@code null}. To set a binary attribute, use an empty
507e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * string as the value.
508e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
509e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        public void setAttribute(String name, String value) {
510e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            set("a=" + name, ':', value);
511e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
512e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
513e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        private void write(StringBuilder buffer) {
514e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            for (int i = 0; i < mOrder.length(); ++i) {
515e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                char type = mOrder.charAt(i);
516e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                for (String line : mLines) {
517e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    if (line.charAt(0) == type) {
518e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                        buffer.append(line).append("\r\n");
519e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    }
520e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                }
521e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            }
522e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
523e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
524e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
525e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Invokes {@link #set} after splitting the line into three parts.
526e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
527e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        private void parse(String line) {
528e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            char type = line.charAt(0);
529e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            if (mOrder.indexOf(type) == -1) {
530e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                return;
531e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            }
532e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            char delimiter = '=';
533e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            if (line.startsWith("a=rtpmap:") || line.startsWith("a=fmtp:")) {
534e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                delimiter = ' ';
535e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            } else if (type == 'b' || type == 'a') {
536e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                delimiter = ':';
537e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            }
538e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            int i = line.indexOf(delimiter);
539e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            if (i == -1) {
540e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                set(line, delimiter, "");
541e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            } else {
542e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                set(line.substring(0, i), delimiter, line.substring(i + 1));
543e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            }
544e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
545e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
546e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
547e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Finds the key with the given prefix and returns its suffix.
548e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
549e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        private String[] cut(String prefix, char delimiter) {
550e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            String[] names = new String[mLines.size()];
551e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            int length = 0;
552e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            for (String line : mLines) {
553e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                if (line.startsWith(prefix)) {
554e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    int i = line.indexOf(delimiter);
555e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    if (i == -1) {
556e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                        i = line.length();
557e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    }
558e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    names[length] = line.substring(prefix.length(), i);
559e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    ++length;
560e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                }
561e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            }
562e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            return Arrays.copyOf(names, length);
563e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
564e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
565e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
566e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Returns the index of the key.
567e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
568e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        private int find(String key, char delimiter) {
569e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            int length = key.length();
570e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            for (int i = mLines.size() - 1; i >= 0; --i) {
571e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                String line = mLines.get(i);
572e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                if (line.startsWith(key) && (line.length() == length ||
573e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                        line.charAt(length) == delimiter)) {
574e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    return i;
575e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                }
576e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            }
577e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            return -1;
578e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
579e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
580e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
581e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Sets the key with the value or removes the key if the value is
582e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * {@code null}.
583e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
584e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        private void set(String key, char delimiter, String value) {
585e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            int index = find(key, delimiter);
586e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            if (value != null) {
587e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                if (value.length() != 0) {
588e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    key = key + delimiter + value;
589e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                }
590e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                if (index == -1) {
591e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    mLines.add(key);
592e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                } else {
593e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                    mLines.set(index, key);
594e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                }
595e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            } else if (index != -1) {
596e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                mLines.remove(index);
597e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            }
598e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
599e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh
600e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        /**
601e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         * Returns the value of the key.
602e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh         */
603e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        private String get(String key, char delimiter) {
604e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            int index = find(key, delimiter);
605e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            if (index == -1) {
606e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh                return null;
607e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            }
608e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            String line = mLines.get(index);
609e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            int length = key.length();
610e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh            return (line.length() == length) ? "" : line.substring(length + 1);
611e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh        }
612e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh    }
613e6c0c109588771a97aba51d06fdf73557b06dfd3Chia-chi Yeh}
614