17d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh/*
27d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * Copyright (C) 2010 The Android Open Source Project
37d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh *
47d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * Licensed under the Apache License, Version 2.0 (the "License");
57d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * you may not use this file except in compliance with the License.
67d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * You may obtain a copy of the License at
77d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh *
87d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh *      http://www.apache.org/licenses/LICENSE-2.0
97d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh *
107d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * Unless required by applicable law or agreed to in writing, software
117d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * distributed under the License is distributed on an "AS IS" BASIS,
127d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * See the License for the specific language governing permissions and
147d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * limitations under the License.
157d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh */
167d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
177d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yehpackage android.net.sip;
187d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
197d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yehimport java.util.ArrayList;
207d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yehimport java.util.Arrays;
210226eb00cecf82b53e29758c04cd1b39015d2909Johan Redestigimport java.util.Locale;
227d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
237d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh/**
247d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * An object used to manipulate messages of Session Description Protocol (SDP).
257d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * It is mainly designed for the uses of Session Initiation Protocol (SIP).
267d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * Therefore, it only handles connection addresses ("c="), bandwidth limits,
277d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * ("b="), encryption keys ("k="), and attribute fields ("a="). Currently this
287d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * implementation does not support multicast sessions.
297d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh *
307d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * <p>Here is an example code to create a session description.</p>
317d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * <pre>
327d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * SimpleSessionDescription description = new SimpleSessionDescription(
337d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh *     System.currentTimeMillis(), "1.2.3.4");
347d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * Media media = description.newMedia("audio", 56789, 1, "RTP/AVP");
357d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * media.setRtpPayload(0, "PCMU/8000", null);
367d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * media.setRtpPayload(8, "PCMA/8000", null);
377d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * media.setRtpPayload(127, "telephone-event/8000", "0-15");
387d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * media.setAttribute("sendrecv", "");
397d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * </pre>
407d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * <p>Invoking <code>description.encode()</code> will produce a result like the
417d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * one below.</p>
427d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * <pre>
437d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * v=0
447d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * o=- 1284970442706 1284970442709 IN IP4 1.2.3.4
457d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * s=-
467d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * c=IN IP4 1.2.3.4
477d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * t=0 0
487d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * m=audio 56789 RTP/AVP 0 8 127
497d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * a=rtpmap:0 PCMU/8000
507d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * a=rtpmap:8 PCMA/8000
517d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * a=rtpmap:127 telephone-event/8000
527d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * a=fmtp:127 0-15
537d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * a=sendrecv
547d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * </pre>
557d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh * @hide
567d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh */
577d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yehpublic class SimpleSessionDescription {
587d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    private final Fields mFields = new Fields("voscbtka");
597d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    private final ArrayList<Media> mMedia = new ArrayList<Media>();
607d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
617d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    /**
627d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * Creates a minimal session description from the given session ID and
637d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * unicast address. The address is used in the origin field ("o=") and the
647d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * connection field ("c="). See {@link SimpleSessionDescription} for an
657d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * example of its usage.
667d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     */
677d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    public SimpleSessionDescription(long sessionId, String address) {
687d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        address = (address.indexOf(':') < 0 ? "IN IP4 " : "IN IP6 ") + address;
697d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        mFields.parse("v=0");
700226eb00cecf82b53e29758c04cd1b39015d2909Johan Redestig        mFields.parse(String.format(Locale.US, "o=- %d %d %s", sessionId,
717d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                System.currentTimeMillis(), address));
727d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        mFields.parse("s=-");
737d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        mFields.parse("t=0 0");
747d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        mFields.parse("c=" + address);
757d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    }
767d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
777d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    /**
787d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * Creates a session description from the given message.
797d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     *
807d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * @throws IllegalArgumentException if message is invalid.
817d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     */
827d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    public SimpleSessionDescription(String message) {
837d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        String[] lines = message.trim().replaceAll(" +", " ").split("[\r\n]+");
847d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        Fields fields = mFields;
857d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
867d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        for (String line : lines) {
877d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            try {
887d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                if (line.charAt(1) != '=') {
897d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    throw new IllegalArgumentException();
907d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                }
917d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                if (line.charAt(0) == 'm') {
927d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    String[] parts = line.substring(2).split(" ", 4);
937d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    String[] ports = parts[1].split("/", 2);
947d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    Media media = newMedia(parts[0], Integer.parseInt(ports[0]),
957d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                            (ports.length < 2) ? 1 : Integer.parseInt(ports[1]),
967d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                            parts[2]);
977d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    for (String format : parts[3].split(" ")) {
987d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                        media.setFormat(format, null);
997d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    }
1007d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    fields = media;
1017d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                } else {
1027d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    fields.parse(line);
1037d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                }
1047d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            } catch (Exception e) {
1057d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                throw new IllegalArgumentException("Invalid SDP: " + line);
1067d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            }
1077d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
1087d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    }
1097d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
1107d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    /**
1117d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * Creates a new media description in this session description.
1127d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     *
1137d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * @param type The media type, e.g. {@code "audio"}.
1147d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * @param port The first transport port used by this media.
1157d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * @param portCount The number of contiguous ports used by this media.
1167d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * @param protocol The transport protocol, e.g. {@code "RTP/AVP"}.
1177d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     */
1187d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    public Media newMedia(String type, int port, int portCount,
1197d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            String protocol) {
1207d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        Media media = new Media(type, port, portCount, protocol);
1217d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        mMedia.add(media);
1227d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        return media;
1237d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    }
1247d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
1257d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    /**
1267d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * Returns all the media descriptions in this session description.
1277d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     */
1287d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    public Media[] getMedia() {
1297d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        return mMedia.toArray(new Media[mMedia.size()]);
1307d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    }
1317d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
1327d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    /**
1337d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * Encodes the session description and all its media descriptions in a
1347d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * string. Note that the result might be incomplete if a required field
1357d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * has never been added before.
1367d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     */
1377d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    public String encode() {
1387d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        StringBuilder buffer = new StringBuilder();
1397d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        mFields.write(buffer);
1407d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        for (Media media : mMedia) {
1417d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            media.write(buffer);
1427d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
1437d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        return buffer.toString();
1447d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    }
1457d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
1467d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    /**
1477d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * Returns the connection address or {@code null} if it is not present.
1487d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     */
1497d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    public String getAddress() {
1507d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        return mFields.getAddress();
1517d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    }
1527d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
1537d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    /**
1547d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * Sets the connection address. The field will be removed if the address
1557d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * is {@code null}.
1567d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     */
1577d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    public void setAddress(String address) {
1587d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        mFields.setAddress(address);
1597d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    }
1607d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
1617d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    /**
1627d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * Returns the encryption method or {@code null} if it is not present.
1637d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     */
1647d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    public String getEncryptionMethod() {
1657d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        return mFields.getEncryptionMethod();
1667d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    }
1677d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
1687d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    /**
1697d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * Returns the encryption key or {@code null} if it is not present.
1707d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     */
1717d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    public String getEncryptionKey() {
1727d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        return mFields.getEncryptionKey();
1737d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    }
1747d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
1757d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    /**
1767d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * Sets the encryption method and the encryption key. The field will be
1777d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * removed if the method is {@code null}.
1787d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     */
1797d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    public void setEncryption(String method, String key) {
1807d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        mFields.setEncryption(method, key);
1817d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    }
1827d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
1837d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    /**
1847d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * Returns the types of the bandwidth limits.
1857d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     */
1867d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    public String[] getBandwidthTypes() {
1877d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        return mFields.getBandwidthTypes();
1887d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    }
1897d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
1907d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    /**
1917d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * Returns the bandwidth limit of the given type or {@code -1} if it is not
1927d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * present.
1937d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     */
1947d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    public int getBandwidth(String type) {
1957d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        return mFields.getBandwidth(type);
1967d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    }
1977d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
1987d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    /**
1997d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * Sets the bandwith limit for the given type. The field will be removed if
2007d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * the value is negative.
2017d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     */
2027d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    public void setBandwidth(String type, int value) {
2037d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        mFields.setBandwidth(type, value);
2047d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    }
2057d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
2067d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    /**
2077d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * Returns the names of all the attributes.
2087d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     */
2097d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    public String[] getAttributeNames() {
2107d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        return mFields.getAttributeNames();
2117d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    }
2127d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
2137d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    /**
2147d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * Returns the attribute of the given name or {@code null} if it is not
2157d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * present.
2167d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     */
2177d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    public String getAttribute(String name) {
2187d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        return mFields.getAttribute(name);
2197d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    }
2207d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
2217d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    /**
2227d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * Sets the attribute for the given name. The field will be removed if
2237d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * the value is {@code null}. To set a binary attribute, use an empty
2247d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * string as the value.
2257d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     */
2267d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    public void setAttribute(String name, String value) {
2277d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        mFields.setAttribute(name, value);
2287d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    }
2297d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
2307d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    /**
2317d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * This class represents a media description of a session description. It
2327d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * can only be created by {@link SimpleSessionDescription#newMedia}. Since
2337d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * the syntax is more restricted for RTP based protocols, two sets of access
2347d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * methods are implemented. See {@link SimpleSessionDescription} for an
2357d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * example of its usage.
2367d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     */
2377d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    public static class Media extends Fields {
2387d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        private final String mType;
2397d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        private final int mPort;
2407d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        private final int mPortCount;
2417d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        private final String mProtocol;
2427d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        private ArrayList<String> mFormats = new ArrayList<String>();
2437d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
2447d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        private Media(String type, int port, int portCount, String protocol) {
2457d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            super("icbka");
2467d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            mType = type;
2477d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            mPort = port;
2487d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            mPortCount = portCount;
2497d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            mProtocol = protocol;
2507d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
2517d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
2527d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
2537d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Returns the media type.
2547d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
2557d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public String getType() {
2567d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            return mType;
2577d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
2587d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
2597d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
2607d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Returns the first transport port used by this media.
2617d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
2627d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public int getPort() {
2637d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            return mPort;
2647d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
2657d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
2667d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
2677d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Returns the number of contiguous ports used by this media.
2687d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
2697d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public int getPortCount() {
2707d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            return mPortCount;
2717d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
2727d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
2737d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
2747d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Returns the transport protocol.
2757d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
2767d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public String getProtocol() {
2777d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            return mProtocol;
2787d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
2797d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
2807d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
2817d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Returns the media formats.
2827d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
2837d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public String[] getFormats() {
2847d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            return mFormats.toArray(new String[mFormats.size()]);
2857d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
2867d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
2877d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
2887d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Returns the {@code fmtp} attribute of the given format or
2897d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * {@code null} if it is not present.
2907d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
2917d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public String getFmtp(String format) {
2927d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            return super.get("a=fmtp:" + format, ' ');
2937d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
2947d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
2957d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
2967d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Sets a format and its {@code fmtp} attribute. If the attribute is
2977d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * {@code null}, the corresponding field will be removed.
2987d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
2997d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public void setFormat(String format, String fmtp) {
3007d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            mFormats.remove(format);
3017d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            mFormats.add(format);
3027d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            super.set("a=rtpmap:" + format, ' ', null);
3037d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            super.set("a=fmtp:" + format, ' ', fmtp);
3047d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
3057d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
3067d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
3077d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Removes a format and its {@code fmtp} attribute.
3087d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
3097d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public void removeFormat(String format) {
3107d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            mFormats.remove(format);
3117d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            super.set("a=rtpmap:" + format, ' ', null);
3127d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            super.set("a=fmtp:" + format, ' ', null);
3137d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
3147d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
3157d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
3167d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Returns the RTP payload types.
3177d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
3187d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public int[] getRtpPayloadTypes() {
3197d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            int[] types = new int[mFormats.size()];
3207d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            int length = 0;
3217d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            for (String format : mFormats) {
3227d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                try {
3237d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    types[length] = Integer.parseInt(format);
3247d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    ++length;
3257d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                } catch (NumberFormatException e) { }
3267d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            }
3277d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            return Arrays.copyOf(types, length);
3287d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
3297d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
3307d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
3317d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Returns the {@code rtpmap} attribute of the given RTP payload type
3327d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * or {@code null} if it is not present.
3337d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
3347d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public String getRtpmap(int type) {
3357d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            return super.get("a=rtpmap:" + type, ' ');
3367d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
3377d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
3387d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
3397d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Returns the {@code fmtp} attribute of the given RTP payload type or
3407d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * {@code null} if it is not present.
3417d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
3427d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public String getFmtp(int type) {
3437d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            return super.get("a=fmtp:" + type, ' ');
3447d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
3457d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
3467d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
347129cdb5fa30ddc244d0a2dd3b0bc651611b39b8crepo sync         * Sets a RTP payload type and its {@code rtpmap} and {@code fmtp}
3487d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * attributes. If any of the attributes is {@code null}, the
3497d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * corresponding field will be removed. See
3507d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * {@link SimpleSessionDescription} for an example of its usage.
3517d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
3527d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public void setRtpPayload(int type, String rtpmap, String fmtp) {
3537d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            String format = String.valueOf(type);
3547d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            mFormats.remove(format);
3557d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            mFormats.add(format);
3567d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            super.set("a=rtpmap:" + format, ' ', rtpmap);
3577d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            super.set("a=fmtp:" + format, ' ', fmtp);
3587d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
3597d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
3607d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
3617d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Removes a RTP payload and its {@code rtpmap} and {@code fmtp}
3627d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * attributes.
3637d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
3647d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public void removeRtpPayload(int type) {
3657d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            removeFormat(String.valueOf(type));
3667d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
3677d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
3687d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        private void write(StringBuilder buffer) {
3697d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            buffer.append("m=").append(mType).append(' ').append(mPort);
3707d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            if (mPortCount != 1) {
3717d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                buffer.append('/').append(mPortCount);
3727d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            }
3737d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            buffer.append(' ').append(mProtocol);
3747d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            for (String format : mFormats) {
3757d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                buffer.append(' ').append(format);
3767d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            }
3777d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            buffer.append("\r\n");
3787d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            super.write(buffer);
3797d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
3807d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    }
3817d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
3827d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    /**
3837d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * This class acts as a set of fields, and the size of the set is expected
3847d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * to be small. Therefore, it uses a simple list instead of maps. Each field
3857d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * has three parts: a key, a delimiter, and a value. Delimiters are special
3867d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * because they are not included in binary attributes. As a result, the
3877d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * private methods, which are the building blocks of this class, all take
3887d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     * the delimiter as an argument.
3897d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh     */
3907d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    private static class Fields {
3917d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        private final String mOrder;
3927d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        private final ArrayList<String> mLines = new ArrayList<String>();
3937d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
3947d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        Fields(String order) {
3957d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            mOrder = order;
3967d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
3977d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
3987d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
3997d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Returns the connection address or {@code null} if it is not present.
4007d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
4017d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public String getAddress() {
4027d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            String address = get("c", '=');
4037d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            if (address == null) {
4047d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                return null;
4057d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            }
4067d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            String[] parts = address.split(" ");
4077d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            if (parts.length != 3) {
4087d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                return null;
4097d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            }
4107d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            int slash = parts[2].indexOf('/');
4117d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            return (slash < 0) ? parts[2] : parts[2].substring(0, slash);
4127d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
4137d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
4147d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
4157d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Sets the connection address. The field will be removed if the address
4167d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * is {@code null}.
4177d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
4187d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public void setAddress(String address) {
4197d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            if (address != null) {
4207d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                address = (address.indexOf(':') < 0 ? "IN IP4 " : "IN IP6 ") +
4217d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                        address;
4227d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            }
4237d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            set("c", '=', address);
4247d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
4257d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
4267d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
4277d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Returns the encryption method or {@code null} if it is not present.
4287d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
4297d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public String getEncryptionMethod() {
4307d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            String encryption = get("k", '=');
4317d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            if (encryption == null) {
4327d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                return null;
4337d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            }
4347d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            int colon = encryption.indexOf(':');
4357d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            return (colon == -1) ? encryption : encryption.substring(0, colon);
4367d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
4377d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
4387d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
4397d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Returns the encryption key or {@code null} if it is not present.
4407d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
4417d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public String getEncryptionKey() {
4427d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            String encryption = get("k", '=');
4437d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            if (encryption == null) {
4447d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                return null;
4457d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            }
4467d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            int colon = encryption.indexOf(':');
4477d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            return (colon == -1) ? null : encryption.substring(0, colon + 1);
4487d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
4497d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
4507d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
4517d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Sets the encryption method and the encryption key. The field will be
4527d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * removed if the method is {@code null}.
4537d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
4547d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public void setEncryption(String method, String key) {
4557d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            set("k", '=', (method == null || key == null) ?
4567d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    method : method + ':' + key);
4577d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
4587d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
4597d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
4607d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Returns the types of the bandwidth limits.
4617d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
4627d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public String[] getBandwidthTypes() {
4637d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            return cut("b=", ':');
4647d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
4657d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
4667d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
4677d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Returns the bandwidth limit of the given type or {@code -1} if it is
4687d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * not present.
4697d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
4707d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public int getBandwidth(String type) {
4717d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            String value = get("b=" + type, ':');
4727d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            if (value != null) {
4737d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                try {
4747d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    return Integer.parseInt(value);
4757d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                } catch (NumberFormatException e) { }
4767d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                setBandwidth(type, -1);
4777d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            }
4787d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            return -1;
4797d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
4807d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
4817d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
4827d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Sets the bandwith limit for the given type. The field will be removed
4837d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * if the value is negative.
4847d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
4857d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public void setBandwidth(String type, int value) {
4867d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            set("b=" + type, ':', (value < 0) ? null : String.valueOf(value));
4877d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
4887d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
4897d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
4907d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Returns the names of all the attributes.
4917d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
4927d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public String[] getAttributeNames() {
4937d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            return cut("a=", ':');
4947d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
4957d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
4967d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
4977d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Returns the attribute of the given name or {@code null} if it is not
4987d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * present.
4997d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
5007d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public String getAttribute(String name) {
5017d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            return get("a=" + name, ':');
5027d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
5037d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
5047d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
5057d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Sets the attribute for the given name. The field will be removed if
5067d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * the value is {@code null}. To set a binary attribute, use an empty
5077d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * string as the value.
5087d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
5097d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        public void setAttribute(String name, String value) {
5107d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            set("a=" + name, ':', value);
5117d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
5127d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
5137d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        private void write(StringBuilder buffer) {
5147d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            for (int i = 0; i < mOrder.length(); ++i) {
5157d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                char type = mOrder.charAt(i);
5167d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                for (String line : mLines) {
5177d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    if (line.charAt(0) == type) {
5187d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                        buffer.append(line).append("\r\n");
5197d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    }
5207d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                }
5217d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            }
5227d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
5237d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
5247d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
5257d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Invokes {@link #set} after splitting the line into three parts.
5267d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
5277d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        private void parse(String line) {
5287d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            char type = line.charAt(0);
5297d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            if (mOrder.indexOf(type) == -1) {
5307d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                return;
5317d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            }
5327d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            char delimiter = '=';
5337d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            if (line.startsWith("a=rtpmap:") || line.startsWith("a=fmtp:")) {
5347d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                delimiter = ' ';
5357d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            } else if (type == 'b' || type == 'a') {
5367d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                delimiter = ':';
5377d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            }
5387d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            int i = line.indexOf(delimiter);
5397d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            if (i == -1) {
5407d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                set(line, delimiter, "");
5417d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            } else {
5427d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                set(line.substring(0, i), delimiter, line.substring(i + 1));
5437d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            }
5447d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
5457d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
5467d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
5477d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Finds the key with the given prefix and returns its suffix.
5487d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
5497d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        private String[] cut(String prefix, char delimiter) {
5507d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            String[] names = new String[mLines.size()];
5517d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            int length = 0;
5527d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            for (String line : mLines) {
5537d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                if (line.startsWith(prefix)) {
5547d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    int i = line.indexOf(delimiter);
5557d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    if (i == -1) {
5567d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                        i = line.length();
5577d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    }
5587d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    names[length] = line.substring(prefix.length(), i);
5597d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    ++length;
5607d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                }
5617d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            }
5627d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            return Arrays.copyOf(names, length);
5637d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
5647d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
5657d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
5667d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Returns the index of the key.
5677d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
5687d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        private int find(String key, char delimiter) {
5697d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            int length = key.length();
5707d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            for (int i = mLines.size() - 1; i >= 0; --i) {
5717d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                String line = mLines.get(i);
5727d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                if (line.startsWith(key) && (line.length() == length ||
5737d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                        line.charAt(length) == delimiter)) {
5747d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    return i;
5757d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                }
5767d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            }
5777d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            return -1;
5787d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
5797d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
5807d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
5817d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Sets the key with the value or removes the key if the value is
5827d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * {@code null}.
5837d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
5847d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        private void set(String key, char delimiter, String value) {
5857d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            int index = find(key, delimiter);
5867d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            if (value != null) {
5877d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                if (value.length() != 0) {
5887d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    key = key + delimiter + value;
5897d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                }
5907d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                if (index == -1) {
5917d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    mLines.add(key);
5927d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                } else {
5937d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                    mLines.set(index, key);
5947d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                }
5957d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            } else if (index != -1) {
5967d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                mLines.remove(index);
5977d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            }
5987d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
5997d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh
6007d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        /**
6017d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         * Returns the value of the key.
6027d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh         */
6037d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        private String get(String key, char delimiter) {
6047d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            int index = find(key, delimiter);
6057d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            if (index == -1) {
6067d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh                return null;
6077d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            }
6087d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            String line = mLines.get(index);
6097d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            int length = key.length();
6107d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh            return (line.length() == length) ? "" : line.substring(length + 1);
6117d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh        }
6127d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh    }
6137d51a25fe714d14df79cf71d06cf83d88b479759Chia-chi Yeh}
614