summaryrefslogtreecommitdiff
path: root/pjlib
diff options
context:
space:
mode:
authorLiong Sauw Ming <ming@teluu.com>2010-08-27 06:46:29 +0000
committerLiong Sauw Ming <ming@teluu.com>2010-08-27 06:46:29 +0000
commita5c285d7fd046c862ef2ba11cd8bef71786db939 (patch)
tree13247c6735d6bb8f6a4b6d5253b39dd8a4b0e6a3 /pjlib
parent41b2998504157f24d6b991d372e77addbed55030 (diff)
Closed ticket #1107: iOS4 background feature
* pjlib: * add support for activesock TCP to work in background mode. * add feature in ioqueue to recreate closed UDP sockets. * pjsip-apps: * ipjsua: add support for iPhone OS 4 background mode * ipjsystest: add support for iPhone OS 4 background mode git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@3299 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjlib')
-rw-r--r--pjlib/include/pj/activesock.h15
-rw-r--r--pjlib/include/pj/compat/os_auto.h.in8
-rw-r--r--pjlib/src/pj/activesock.c81
-rw-r--r--pjlib/src/pj/ioqueue_common_abs.c10
-rw-r--r--pjlib/src/pj/ioqueue_select.c137
-rw-r--r--pjlib/src/pj/sock_bsd.c13
6 files changed, 259 insertions, 5 deletions
diff --git a/pjlib/include/pj/activesock.h b/pjlib/include/pj/activesock.h
index c7fc908e..1ebb5a2e 100644
--- a/pjlib/include/pj/activesock.h
+++ b/pjlib/include/pj/activesock.h
@@ -302,6 +302,21 @@ PJ_DECL(pj_status_t) pj_activesock_create_udp(pj_pool_t *pool,
*/
PJ_DECL(pj_status_t) pj_activesock_close(pj_activesock_t *asock);
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+/**
+ * Set iPhone OS background mode setting. Setting to 1 will enable TCP
+ * active socket to receive incoming data when application is in the
+ * background. Setting to 0 will disable it. Default value of this
+ * setting is PJ_ACTIVESOCK_TCP_IPHONE_OS_BG.
+ *
+ * @param asock The active socket.
+ * @param val The value of background mode setting.
+ *
+ */
+PJ_DECL(void) pj_activesock_set_iphone_os_bg(pj_activesock_t *asock,
+ int val);
+#endif
/**
* Associate arbitrary data with the active socket. Application may
diff --git a/pjlib/include/pj/compat/os_auto.h.in b/pjlib/include/pj/compat/os_auto.h.in
index 6fe3276e..5c5f74bf 100644
--- a/pjlib/include/pj/compat/os_auto.h.in
+++ b/pjlib/include/pj/compat/os_auto.h.in
@@ -169,13 +169,17 @@
/* The type of atomic variable value: */
#undef PJ_ATOMIC_VALUE_TYPE
-/* Append ".local" suffix to the system's hostname? */
#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
# include "TargetConditionals.h"
# if TARGET_OS_IPHONE
# include "Availability.h"
# ifdef __IPHONE_4_0
-# define PJ_GETHOSTNAME_APPEND_LOCAL_SUFFIX 1
+ /* Append ".local" suffix to the system's hostname? (see ticket #1104) */
+# define PJ_GETHOSTNAME_APPEND_LOCAL_SUFFIX 1
+ /* Is multitasking support available? (see ticket #1107) */
+# define PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT 1
+ /* Enable activesock TCP background mode support */
+# define PJ_ACTIVESOCK_TCP_IPHONE_OS_BG 1
# endif
# endif
#endif
diff --git a/pjlib/src/pj/activesock.c b/pjlib/src/pj/activesock.c
index 6da4c7fa..0ba1a790 100644
--- a/pjlib/src/pj/activesock.c
+++ b/pjlib/src/pj/activesock.c
@@ -21,10 +21,16 @@
#include <pj/compat/socket.h>
#include <pj/assert.h>
#include <pj/errno.h>
+#include <pj/log.h>
#include <pj/pool.h>
#include <pj/sock.h>
#include <pj/string.h>
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+# include <CFNetwork/CFNetwork.h>
+#endif
+
#define PJ_ACTIVESOCK_MAX_LOOP 50
@@ -71,7 +77,13 @@ struct pj_activesock_t
unsigned async_count;
unsigned max_loop;
pj_activesock_cb cb;
-
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+ int bg_setting;
+ pj_sock_t sock;
+ CFReadStreamRef readStream;
+#endif
+
struct send_data send_data;
struct read_op *read_op;
@@ -105,6 +117,51 @@ PJ_DEF(void) pj_activesock_cfg_default(pj_activesock_cfg *cfg)
cfg->whole_data = PJ_TRUE;
}
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+static void activesock_destroy_iphone_os_stream(pj_activesock_t *asock)
+{
+ if (asock->readStream) {
+ CFReadStreamClose(asock->readStream);
+ CFRelease(asock->readStream);
+ asock->readStream = NULL;
+ }
+}
+
+static void activesock_create_iphone_os_stream(pj_activesock_t *asock)
+{
+ if (asock->bg_setting && asock->stream_oriented) {
+ activesock_destroy_iphone_os_stream(asock);
+
+ CFStreamCreatePairWithSocket(kCFAllocatorDefault, asock->sock,
+ &asock->readStream, NULL);
+
+ if (!asock->readStream ||
+ CFReadStreamSetProperty(asock->readStream,
+ kCFStreamNetworkServiceType,
+ kCFStreamNetworkServiceTypeVoIP)
+ != TRUE ||
+ CFReadStreamOpen(asock->readStream) != TRUE)
+ {
+ PJ_LOG(2,("", "Failed to configure TCP transport for VoIP "
+ "usage. Background mode will not be supported."));
+
+ activesock_destroy_iphone_os_stream(asock);
+ }
+ }
+}
+
+
+PJ_DEF(void) pj_activesock_set_iphone_os_bg(pj_activesock_t *asock,
+ int val)
+{
+ asock->bg_setting = val;
+ if (asock->bg_setting)
+ activesock_create_iphone_os_stream(asock);
+ else
+ activesock_destroy_iphone_os_stream(asock);
+}
+#endif
PJ_DEF(pj_status_t) pj_activesock_create( pj_pool_t *pool,
pj_sock_t sock,
@@ -156,6 +213,13 @@ PJ_DEF(pj_status_t) pj_activesock_create( pj_pool_t *pool,
pj_ioqueue_set_concurrency(asock->key, opt->concurrency);
}
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+ asock->sock = sock;
+ pj_activesock_set_iphone_os_bg(asock,
+ PJ_ACTIVESOCK_TCP_IPHONE_OS_BG);
+#endif
+
*p_asock = asock;
return PJ_SUCCESS;
}
@@ -215,6 +279,11 @@ PJ_DEF(pj_status_t) pj_activesock_close(pj_activesock_t *asock)
{
PJ_ASSERT_RETURN(asock, PJ_EINVAL);
if (asock->key) {
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+ activesock_destroy_iphone_os_stream(asock);
+#endif
+
pj_ioqueue_unregister(asock->key);
asock->key = NULL;
}
@@ -733,6 +802,10 @@ static void ioqueue_on_accept_complete(pj_ioqueue_key_t *key,
if (!ret)
return;
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+ activesock_create_iphone_os_stream(asock);
+#endif
} else if (status==PJ_SUCCESS) {
/* Application doesn't handle the new socket, we need to
* close it to avoid resource leak.
@@ -775,6 +848,12 @@ static void ioqueue_on_connect_complete(pj_ioqueue_key_t *key,
/* We've been destroyed */
return;
}
+
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+ activesock_create_iphone_os_stream(asock);
+#endif
+
}
}
#endif /* PJ_HAS_TCP */
diff --git a/pjlib/src/pj/ioqueue_common_abs.c b/pjlib/src/pj/ioqueue_common_abs.c
index bcd4f6a3..07ed838c 100644
--- a/pjlib/src/pj/ioqueue_common_abs.c
+++ b/pjlib/src/pj/ioqueue_common_abs.c
@@ -529,6 +529,16 @@ void ioqueue_dispatch_read_event( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h )
/* In any case we would report this to caller. */
bytes_read = -rc;
+
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+ /* Special treatment for dead UDP sockets here, see ticket #1107 */
+ if (rc == PJ_STATUS_FROM_OS(ENOTCONN) && !IS_CLOSING(h) &&
+ h->fd_type==pj_SOCK_DGRAM())
+ {
+ replace_udp_sock(h);
+ }
+#endif
}
/* Unlock; from this point we don't need to hold key's mutex
diff --git a/pjlib/src/pj/ioqueue_select.c b/pjlib/src/pj/ioqueue_select.c
index d4f93728..3a7c14e3 100644
--- a/pjlib/src/pj/ioqueue_select.c
+++ b/pjlib/src/pj/ioqueue_select.c
@@ -37,6 +37,7 @@
#include <pj/sock.h>
#include <pj/compat/socket.h>
#include <pj/sock_select.h>
+#include <pj/sock_qos.h>
#include <pj/errno.h>
/* Now that we have access to OS'es <sys/select>, lets check again that
@@ -123,6 +124,9 @@ struct pj_ioqueue_t
#endif
};
+/* Proto */
+static pj_status_t replace_udp_sock(pj_ioqueue_key_t *h);
+
/* Include implementation for common abstraction after we declare
* pj_ioqueue_key_t and pj_ioqueue_t.
*/
@@ -620,6 +624,139 @@ static void scan_closing_keys(pj_ioqueue_t *ioqueue)
}
#endif
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+static pj_status_t replace_udp_sock(pj_ioqueue_key_t *h)
+{
+ enum flags {
+ HAS_PEER_ADDR = 1,
+ HAS_QOS = 2
+ };
+ pj_sock_t old_sock, new_sock = PJ_INVALID_SOCKET;
+ pj_sockaddr local_addr, rem_addr;
+ int val, addr_len;
+ pj_fd_set_t *fds[3];
+ unsigned i, fds_cnt, flags=0;
+ pj_qos_params qos_params;
+ unsigned msec;
+ pj_status_t status;
+
+ pj_lock_acquire(h->ioqueue->lock);
+
+ old_sock = h->fd;
+
+ /* Can only replace UDP socket */
+ pj_assert(h->fd_type == pj_SOCK_DGRAM());
+
+ PJ_LOG(4,(THIS_FILE, "Attempting to replace UDP socket %d", old_sock));
+
+ /* Investigate the old socket */
+ addr_len = sizeof(local_addr);
+ status = pj_sock_getsockname(old_sock, &local_addr, &addr_len);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ addr_len = sizeof(rem_addr);
+ status = pj_sock_getpeername(old_sock, &rem_addr, &addr_len);
+ if (status == PJ_SUCCESS)
+ flags |= HAS_PEER_ADDR;
+
+ status = pj_sock_get_qos_params(old_sock, &qos_params);
+ if (status == PJ_SUCCESS)
+ flags |= HAS_QOS;
+
+ /* We're done with the old socket, close it otherwise we'll get
+ * error in bind()
+ */
+ pj_sock_close(old_sock);
+
+ /* Prepare the new socket */
+ status = pj_sock_socket(local_addr.addr.sa_family, PJ_SOCK_DGRAM, 0,
+ &new_sock);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Even after the socket is closed, we'll still get "Address in use"
+ * errors, so force it with SO_REUSEADDR
+ */
+ val = 1;
+ status = pj_sock_setsockopt(new_sock, SOL_SOCKET, SO_REUSEADDR,
+ &val, sizeof(val));
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* The loop is silly, but what else can we do? */
+ addr_len = pj_sockaddr_get_len(&local_addr);
+ for (msec=20; ; msec<1000? msec=msec*2 : 1000) {
+ status = pj_sock_bind(new_sock, &local_addr, addr_len);
+ if (status != PJ_STATUS_FROM_OS(EADDRINUSE))
+ break;
+ PJ_LOG(4,(THIS_FILE, "Address is still in use, retrying.."));
+ pj_thread_sleep(msec);
+ }
+
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ if (flags & HAS_QOS) {
+ status = pj_sock_set_qos_params(new_sock, &qos_params);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ }
+
+ if (flags & HAS_PEER_ADDR) {
+ status = pj_sock_connect(new_sock, &rem_addr, addr_len);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ }
+
+ /* Set socket to nonblocking. */
+ val = 1;
+#if defined(PJ_WIN32) && PJ_WIN32!=0 || \
+ defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0
+ if (ioctlsocket(new_sock, FIONBIO, &val)) {
+#else
+ if (ioctl(new_sock, FIONBIO, &val)) {
+#endif
+ status = pj_get_netos_error();
+ goto on_error;
+ }
+
+ /* Replace the occurrence of old socket with new socket in the
+ * fd sets.
+ */
+ fds_cnt = 0;
+ fds[fds_cnt++] = &h->ioqueue->rfdset;
+ fds[fds_cnt++] = &h->ioqueue->wfdset;
+#if PJ_HAS_TCP
+ fds[fds_cnt++] = &h->ioqueue->xfdset;
+#endif
+
+ for (i=0; i<fds_cnt; ++i) {
+ if (PJ_FD_ISSET(old_sock, fds[i])) {
+ PJ_FD_CLR(old_sock, fds[i]);
+ PJ_FD_SET(new_sock, fds[i]);
+ }
+ }
+
+ /* And finally replace the fd in the key */
+ h->fd = new_sock;
+
+ PJ_LOG(4,(THIS_FILE, "UDP has been replaced successfully!"));
+
+ pj_lock_release(h->ioqueue->lock);
+
+ return PJ_SUCCESS;
+
+on_error:
+ if (new_sock != PJ_INVALID_SOCKET)
+ pj_sock_close(new_sock);
+ PJ_PERROR(1,(THIS_FILE, status, "Error replacing socket"));
+ pj_lock_release(h->ioqueue->lock);
+ return status;
+}
+#endif
+
/*
* pj_ioqueue_poll()
diff --git a/pjlib/src/pj/sock_bsd.c b/pjlib/src/pj/sock_bsd.c
index 2551e641..aef2ff8a 100644
--- a/pjlib/src/pj/sock_bsd.c
+++ b/pjlib/src/pj/sock_bsd.c
@@ -513,12 +513,21 @@ PJ_DEF(pj_status_t) pj_sock_socket(int af,
PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL);
PJ_ASSERT_RETURN(PJ_INVALID_SOCKET==-1,
(*sock=PJ_INVALID_SOCKET, PJ_EINVAL));
-
+
*sock = socket(af, type, proto);
if (*sock == PJ_INVALID_SOCKET)
return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
- else
+ else {
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+ PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+ pj_int32_t val = 1;
+ if (type == pj_SOCK_DGRAM()) {
+ pj_sock_setsockopt(*sock, pj_SOL_SOCKET(), SO_NOSIGPIPE,
+ &val, sizeof(val));
+ }
+#endif
return PJ_SUCCESS;
+ }
}
#endif