From a5c285d7fd046c862ef2ba11cd8bef71786db939 Mon Sep 17 00:00:00 2001 From: Liong Sauw Ming Date: Fri, 27 Aug 2010 06:46:29 +0000 Subject: 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 --- pjlib/src/pj/activesock.c | 81 +++++++++++++++++++++- pjlib/src/pj/ioqueue_common_abs.c | 10 +++ pjlib/src/pj/ioqueue_select.c | 137 ++++++++++++++++++++++++++++++++++++++ pjlib/src/pj/sock_bsd.c | 13 +++- 4 files changed, 238 insertions(+), 3 deletions(-) (limited to 'pjlib/src') 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 #include #include +#include #include #include #include +#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \ + PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0 +# include +#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 #include #include +#include #include /* Now that we have access to OS'es , 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; ifd = 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 -- cgit v1.2.3