1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net.sip;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21import android.text.TextUtils;
22
23import java.io.ObjectStreamException;
24import java.io.Serializable;
25import java.text.ParseException;
26import javax.sip.InvalidArgumentException;
27import javax.sip.ListeningPoint;
28import javax.sip.PeerUnavailableException;
29import javax.sip.SipFactory;
30import javax.sip.address.Address;
31import javax.sip.address.AddressFactory;
32import javax.sip.address.SipURI;
33import javax.sip.address.URI;
34
35/**
36 * Defines a SIP profile, including a SIP account, domain and server information.
37 * <p>You can create a {@link SipProfile} using {@link
38 * SipProfile.Builder}. You can also retrieve one from a {@link SipSession}, using {@link
39 * SipSession#getLocalProfile} and {@link SipSession#getPeerProfile}.</p>
40 *
41 * <div class="special reference">
42 * <h3>Developer Guides</h3>
43 * <p>For more information about using SIP, read the
44 * <a href="{@docRoot}guide/topics/network/sip.html">Session Initiation Protocol</a>
45 * developer guide.</p>
46 * </div>
47 */
48public class SipProfile implements Parcelable, Serializable, Cloneable {
49    private static final long serialVersionUID = 1L;
50    private static final int DEFAULT_PORT = 5060;
51    private static final String TCP = "TCP";
52    private static final String UDP = "UDP";
53    private Address mAddress;
54    private String mProxyAddress;
55    private String mPassword;
56    private String mDomain;
57    private String mProtocol = UDP;
58    private String mProfileName;
59    private String mAuthUserName;
60    private int mPort = DEFAULT_PORT;
61    private boolean mSendKeepAlive = false;
62    private boolean mAutoRegistration = true;
63    private transient int mCallingUid = 0;
64
65    public static final Parcelable.Creator<SipProfile> CREATOR =
66            new Parcelable.Creator<SipProfile>() {
67                public SipProfile createFromParcel(Parcel in) {
68                    return new SipProfile(in);
69                }
70
71                public SipProfile[] newArray(int size) {
72                    return new SipProfile[size];
73                }
74            };
75
76    /**
77     * Helper class for creating a {@link SipProfile}.
78     */
79    public static class Builder {
80        private AddressFactory mAddressFactory;
81        private SipProfile mProfile = new SipProfile();
82        private SipURI mUri;
83        private String mDisplayName;
84        private String mProxyAddress;
85
86        {
87            try {
88                mAddressFactory =
89                        SipFactory.getInstance().createAddressFactory();
90            } catch (PeerUnavailableException e) {
91                throw new RuntimeException(e);
92            }
93        }
94
95        /**
96         * Creates a builder based on the given profile.
97         */
98        public Builder(SipProfile profile) {
99            if (profile == null) throw new NullPointerException();
100            try {
101                mProfile = (SipProfile) profile.clone();
102            } catch (CloneNotSupportedException e) {
103                throw new RuntimeException("should not occur", e);
104            }
105            mProfile.mAddress = null;
106            mUri = profile.getUri();
107            mUri.setUserPassword(profile.getPassword());
108            mDisplayName = profile.getDisplayName();
109            mProxyAddress = profile.getProxyAddress();
110            mProfile.mPort = profile.getPort();
111        }
112
113        /**
114         * Constructor.
115         *
116         * @param uriString the URI string as "sip:<user_name>@<domain>"
117         * @throws ParseException if the string is not a valid URI
118         */
119        public Builder(String uriString) throws ParseException {
120            if (uriString == null) {
121                throw new NullPointerException("uriString cannot be null");
122            }
123            URI uri = mAddressFactory.createURI(fix(uriString));
124            if (uri instanceof SipURI) {
125                mUri = (SipURI) uri;
126            } else {
127                throw new ParseException(uriString + " is not a SIP URI", 0);
128            }
129            mProfile.mDomain = mUri.getHost();
130        }
131
132        /**
133         * Constructor.
134         *
135         * @param username username of the SIP account
136         * @param serverDomain the SIP server domain; if the network address
137         *      is different from the domain, use {@link #setOutboundProxy} to
138         *      set server address
139         * @throws ParseException if the parameters are not valid
140         */
141        public Builder(String username, String serverDomain)
142                throws ParseException {
143            if ((username == null) || (serverDomain == null)) {
144                throw new NullPointerException(
145                        "username and serverDomain cannot be null");
146            }
147            mUri = mAddressFactory.createSipURI(username, serverDomain);
148            mProfile.mDomain = serverDomain;
149        }
150
151        private String fix(String uriString) {
152            return (uriString.trim().toLowerCase().startsWith("sip:")
153                    ? uriString
154                    : "sip:" + uriString);
155        }
156
157        /**
158         * Sets the username used for authentication.
159         *
160         * @param name authentication username of the profile
161         * @return this builder object
162         */
163        public Builder setAuthUserName(String name) {
164            mProfile.mAuthUserName = name;
165            return this;
166        }
167
168        /**
169         * Sets the name of the profile. This name is given by user.
170         *
171         * @param name name of the profile
172         * @return this builder object
173         */
174        public Builder setProfileName(String name) {
175            mProfile.mProfileName = name;
176            return this;
177        }
178
179        /**
180         * Sets the password of the SIP account
181         *
182         * @param password password of the SIP account
183         * @return this builder object
184         */
185        public Builder setPassword(String password) {
186            mUri.setUserPassword(password);
187            return this;
188        }
189
190        /**
191         * Sets the port number of the server. By default, it is 5060.
192         *
193         * @param port port number of the server
194         * @return this builder object
195         * @throws IllegalArgumentException if the port number is out of range
196         */
197        public Builder setPort(int port) throws IllegalArgumentException {
198            if ((port > 65535) || (port < 1000)) {
199                throw new IllegalArgumentException("incorrect port arugment: " + port);
200            }
201            mProfile.mPort = port;
202            return this;
203        }
204
205        /**
206         * Sets the protocol used to connect to the SIP server. Currently,
207         * only "UDP" and "TCP" are supported.
208         *
209         * @param protocol the protocol string
210         * @return this builder object
211         * @throws IllegalArgumentException if the protocol is not recognized
212         */
213        public Builder setProtocol(String protocol)
214                throws IllegalArgumentException {
215            if (protocol == null) {
216                throw new NullPointerException("protocol cannot be null");
217            }
218            protocol = protocol.toUpperCase();
219            if (!protocol.equals(UDP) && !protocol.equals(TCP)) {
220                throw new IllegalArgumentException(
221                        "unsupported protocol: " + protocol);
222            }
223            mProfile.mProtocol = protocol;
224            return this;
225        }
226
227        /**
228         * Sets the outbound proxy of the SIP server.
229         *
230         * @param outboundProxy the network address of the outbound proxy
231         * @return this builder object
232         */
233        public Builder setOutboundProxy(String outboundProxy) {
234            mProxyAddress = outboundProxy;
235            return this;
236        }
237
238        /**
239         * Sets the display name of the user.
240         *
241         * @param displayName display name of the user
242         * @return this builder object
243         */
244        public Builder setDisplayName(String displayName) {
245            mDisplayName = displayName;
246            return this;
247        }
248
249        /**
250         * Sets the send keep-alive flag.
251         *
252         * @param flag true if sending keep-alive message is required,
253         *      false otherwise
254         * @return this builder object
255         */
256        public Builder setSendKeepAlive(boolean flag) {
257            mProfile.mSendKeepAlive = flag;
258            return this;
259        }
260
261
262        /**
263         * Sets the auto. registration flag.
264         *
265         * @param flag true if the profile will be registered automatically,
266         *      false otherwise
267         * @return this builder object
268         */
269        public Builder setAutoRegistration(boolean flag) {
270            mProfile.mAutoRegistration = flag;
271            return this;
272        }
273
274        /**
275         * Builds and returns the SIP profile object.
276         *
277         * @return the profile object created
278         */
279        public SipProfile build() {
280            // remove password from URI
281            mProfile.mPassword = mUri.getUserPassword();
282            mUri.setUserPassword(null);
283            try {
284                if (!TextUtils.isEmpty(mProxyAddress)) {
285                    SipURI uri = (SipURI)
286                            mAddressFactory.createURI(fix(mProxyAddress));
287                    mProfile.mProxyAddress = uri.getHost();
288                } else {
289                    if (!mProfile.mProtocol.equals(UDP)) {
290                        mUri.setTransportParam(mProfile.mProtocol);
291                    }
292                    if (mProfile.mPort != DEFAULT_PORT) {
293                        mUri.setPort(mProfile.mPort);
294                    }
295                }
296                mProfile.mAddress = mAddressFactory.createAddress(
297                        mDisplayName, mUri);
298            } catch (InvalidArgumentException e) {
299                throw new RuntimeException(e);
300            } catch (ParseException e) {
301                // must not occur
302                throw new RuntimeException(e);
303            }
304            return mProfile;
305        }
306    }
307
308    private SipProfile() {
309    }
310
311    private SipProfile(Parcel in) {
312        mAddress = (Address) in.readSerializable();
313        mProxyAddress = in.readString();
314        mPassword = in.readString();
315        mDomain = in.readString();
316        mProtocol = in.readString();
317        mProfileName = in.readString();
318        mSendKeepAlive = (in.readInt() == 0) ? false : true;
319        mAutoRegistration = (in.readInt() == 0) ? false : true;
320        mCallingUid = in.readInt();
321        mPort = in.readInt();
322        mAuthUserName = in.readString();
323    }
324
325    @Override
326    public void writeToParcel(Parcel out, int flags) {
327        out.writeSerializable(mAddress);
328        out.writeString(mProxyAddress);
329        out.writeString(mPassword);
330        out.writeString(mDomain);
331        out.writeString(mProtocol);
332        out.writeString(mProfileName);
333        out.writeInt(mSendKeepAlive ? 1 : 0);
334        out.writeInt(mAutoRegistration ? 1 : 0);
335        out.writeInt(mCallingUid);
336        out.writeInt(mPort);
337        out.writeString(mAuthUserName);
338    }
339
340    @Override
341    public int describeContents() {
342        return 0;
343    }
344
345    /**
346     * Gets the SIP URI of this profile.
347     *
348     * @return the SIP URI of this profile
349     * @hide
350     */
351    public SipURI getUri() {
352        return (SipURI) mAddress.getURI();
353    }
354
355    /**
356     * Gets the SIP URI string of this profile.
357     *
358     * @return the SIP URI string of this profile
359     */
360    public String getUriString() {
361        // We need to return the sip uri domain instead of
362        // the SIP URI with transport, port information if
363        // the outbound proxy address exists.
364        if (!TextUtils.isEmpty(mProxyAddress)) {
365            return "sip:" + getUserName() + "@" + mDomain;
366        }
367        return getUri().toString();
368    }
369
370    /**
371     * Gets the SIP address of this profile.
372     *
373     * @return the SIP address of this profile
374     * @hide
375     */
376    public Address getSipAddress() {
377        return mAddress;
378    }
379
380    /**
381     * Gets the display name of the user.
382     *
383     * @return the display name of the user
384     */
385    public String getDisplayName() {
386        return mAddress.getDisplayName();
387    }
388
389    /**
390     * Gets the username.
391     *
392     * @return the username
393     */
394    public String getUserName() {
395        return getUri().getUser();
396    }
397
398    /**
399     * Gets the username for authentication. If it is null, then the username
400     * is used in authentication instead.
401     *
402     * @return the authentication username
403     * @see #getUserName
404     */
405    public String getAuthUserName() {
406        return mAuthUserName;
407    }
408
409    /**
410     * Gets the password.
411     *
412     * @return the password
413     */
414    public String getPassword() {
415        return mPassword;
416    }
417
418    /**
419     * Gets the SIP domain.
420     *
421     * @return the SIP domain
422     */
423    public String getSipDomain() {
424        return mDomain;
425    }
426
427    /**
428     * Gets the port number of the SIP server.
429     *
430     * @return the port number of the SIP server
431     */
432    public int getPort() {
433        return mPort;
434    }
435
436    /**
437     * Gets the protocol used to connect to the server.
438     *
439     * @return the protocol
440     */
441    public String getProtocol() {
442        return mProtocol;
443    }
444
445    /**
446     * Gets the network address of the server outbound proxy.
447     *
448     * @return the network address of the server outbound proxy
449     */
450    public String getProxyAddress() {
451        return mProxyAddress;
452    }
453
454    /**
455     * Gets the (user-defined) name of the profile.
456     *
457     * @return name of the profile
458     */
459    public String getProfileName() {
460        return mProfileName;
461    }
462
463    /**
464     * Gets the flag of 'Sending keep-alive'.
465     *
466     * @return the flag of sending SIP keep-alive messages.
467     */
468    public boolean getSendKeepAlive() {
469        return mSendKeepAlive;
470    }
471
472    /**
473     * Gets the flag of 'Auto Registration'.
474     *
475     * @return the flag of registering the profile automatically.
476     */
477    public boolean getAutoRegistration() {
478        return mAutoRegistration;
479    }
480
481    /**
482     * Sets the calling process's Uid in the sip service.
483     * @hide
484     */
485    public void setCallingUid(int uid) {
486        mCallingUid = uid;
487    }
488
489    /**
490     * Gets the calling process's Uid in the sip settings.
491     * @hide
492     */
493    public int getCallingUid() {
494        return mCallingUid;
495    }
496
497    private Object readResolve() throws ObjectStreamException {
498        // For compatibility.
499        if (mPort == 0) mPort = DEFAULT_PORT;
500        return this;
501    }
502}
503