diff options
Diffstat (limited to 'telephony/remote_call.c')
-rw-r--r-- | telephony/remote_call.c | 430 |
1 files changed, 430 insertions, 0 deletions
diff --git a/telephony/remote_call.c b/telephony/remote_call.c new file mode 100644 index 0000000..927e11d --- /dev/null +++ b/telephony/remote_call.c @@ -0,0 +1,430 @@ +/* Copyright (C) 2007-2008 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ +#include "remote_call.h" +#include "android/utils/bufprint.h" +#include "android/utils/debug.h" +#include "sysdeps.h" +#include "gsm.h" +#include "android/android.h" +#include "sockets.h" +#include <stdlib.h> + +#define DEBUG 1 + +#if 1 +# define D_ACTIVE VERBOSE_CHECK(modem) +#else +# define D_ACTIVE DEBUG +#endif + +#if 1 +# define S_ACTIVE VERBOSE_CHECK(socket) +#else +# define S_ACTIVE DEBUG +#endif + +#if DEBUG +# include <stdio.h> +# define D(...) do { if (D_ACTIVE) fprintf( stderr, __VA_ARGS__ ); } while (0) +# define S(...) do { if (S_ACTIVE) fprintf( stderr, __VA_ARGS__ ); } while (0) +#else +# define D(...) ((void)0) +# define S(...) ((void)0) +#endif + +/** By convention, remote numbers are the console ports, i.e. 5554, 5556, etc... + **/ +#define REMOTE_NUMBER_BASE 5554 +#define REMOTE_NUMBER_MAX 16 +#define REMOTE_NUMBER_MAX_CHARS 4 +#define REMOTE_CONSOLE_PORT 5554 + +int +remote_number_from_port( int port ) +{ + if (port & 1) /* must be even */ + return -1; + + port = (port - REMOTE_CONSOLE_PORT) >> 1; + if ((unsigned)port >= REMOTE_NUMBER_MAX) + return -1; + + return REMOTE_NUMBER_BASE + port*2; +} + +int +remote_number_to_port( int number ) +{ + if (number & 1) /* must be even */ + return -1; + + number = (number - REMOTE_NUMBER_BASE) >> 1; + if ((unsigned)number >= REMOTE_NUMBER_MAX) + return -1; + + return REMOTE_CONSOLE_PORT + number*2; +} + +int +remote_number_string_to_port( const char* number ) +{ + char* end; + long num = strtol( number, &end, 10 ); + + if (end == NULL || *end || (int)num != num ) + return -1; + + return remote_number_to_port( (int)num ); +} + +/** REMOTE CALL OBJECTS + **/ + +typedef struct RemoteCallRec { + struct RemoteCallRec* next; + struct RemoteCallRec** pref; + RemoteCallType type; + int to_port; + int from_port; + SysChannel channel; + RemoteResultFunc result_func; + void* result_opaque; + + char quitting; + + /* the output buffer */ + char* buff; + int buff_pos; + int buff_len; + int buff_size; + char buff0[32]; + +} RemoteCallRec, *RemoteCall; + +static void +remote_call_done( RemoteCall call ) +{ + call->pref[0] = call->next; + call->next = NULL; + call->pref = &call->next; + + if (call->buff && call->buff != call->buff0) { + free(call->buff); + call->buff = call->buff0; + call->buff_size = (int) sizeof(call->buff0); + } + + if ( call->channel ) { + sys_channel_close( call->channel ); + call->channel = NULL; + } + + call->buff_pos = 0; + call->buff_len = 0; +} + + +static void +remote_call_free( RemoteCall call ) +{ + if (call) { + remote_call_done( call ); + free(call); + } +} + + +static void remote_call_event( void* opaque, int events ); /* forward */ + +static RemoteCall +remote_call_alloc( RemoteCallType type, int to_port, int from_port ) +{ + RemoteCall rcall = calloc( sizeof(*rcall), 1 ); + int from_num = remote_number_from_port(from_port); + + if (rcall != NULL) { + char *p, *end; + + rcall->pref = &rcall->next; + rcall->type = type; + rcall->to_port = to_port; + rcall->from_port = from_port; + rcall->buff = rcall->buff0; + rcall->buff_size = sizeof(rcall->buff0); + rcall->buff_pos = 0; + + p = rcall->buff; + end = p + rcall->buff_size; + + switch (type) { + case REMOTE_CALL_DIAL: + p = bufprint(p, end, "gsm call %d\n", from_num ); + break; + + case REMOTE_CALL_BUSY: + p = bufprint(p, end, "gsm busy %d\n", from_num); + break; + + case REMOTE_CALL_HOLD: + p = bufprint(p, end, "gsm hold %d\n", from_num); + break; + + case REMOTE_CALL_ACCEPT: + p = bufprint(p, end, "gsm accept %d\n", from_num); + break; + + case REMOTE_CALL_HANGUP: + p = bufprint(p, end, "gsm cancel %d\n", from_num ); + break; + + default: + ; + } + if (p >= end) { + D("%s: buffer too short\n", __FUNCTION__ ); + remote_call_free(rcall); + return NULL; + } + + rcall->buff_len = p - rcall->buff; + + rcall->channel = sys_channel_create_tcp_client( "localhost", to_port ); + if (rcall->channel == NULL) { + D("%s: could not create channel to port %d\n", __FUNCTION__, to_port); + remote_call_free(rcall); + return NULL; + } + + sys_channel_on( rcall->channel, SYS_EVENT_WRITE, remote_call_event, rcall ); + } + return rcall; +} + + +static int +remote_call_set_sms_pdu( RemoteCall call, + SmsPDU pdu ) +{ + char *p, *end; + int msg2len; + + msg2len = 32 + smspdu_to_hex( pdu, NULL, 0 ); + if (msg2len > call->buff_size) { + char* old_buff = call->buff == call->buff0 ? NULL : call->buff; + char* new_buff = realloc( old_buff, msg2len ); + if (new_buff == NULL) { + D("%s: not enough memory to alloc %d bytes", __FUNCTION__, msg2len); + return -1; + } + call->buff = new_buff; + call->buff_size = msg2len; + } + + p = call->buff; + end = p + call->buff_size; + + p = bufprint(p, end, "sms pdu "); + p += smspdu_to_hex( pdu, p, end-p ); + *p++ = '\n'; + *p = 0; + + call->buff_len = p - call->buff; + call->buff_pos = 0; + return 0; +} + + +static void +remote_call_add( RemoteCall call, + RemoteCall *plist ) +{ + RemoteCall first = *plist; + + call->next = first; + call->pref = plist; + + if (first) + first->pref = &call->next; +} + +static void +remote_call_event( void* opaque, int events ) +{ + RemoteCall call = opaque; + + S("%s: called for call (%d,%d), events=%02x\n", __FUNCTION__, + call->from_port, call->to_port, events); + + if (events & SYS_EVENT_READ) { + /* simply drain the channel */ + char temp[32]; + int n = sys_channel_read( call->channel, temp, sizeof(temp) ); + if (n <= 0) { + /* remote emulator probably quitted */ + //S("%s: emulator %d quitted with %d: %s\n", __FUNCTION__, call->to_port, errno, errno_str); + remote_call_free( call ); + return; + } + } + + if (events & SYS_EVENT_WRITE) { + int n; + + if (S_ACTIVE) { + int nn; + S("%s: call (%d,%d) sending %d bytes '", __FUNCTION__, + call->from_port, call->to_port, call->buff_len - call->buff_pos ); + for (nn = call->buff_pos; nn < call->buff_len; nn++) { + int c = call->buff[nn]; + if (c < 32) { + if (c == '\n') + S("\\n"); + else if (c == '\t') + S("\\t"); + else if (c == '\r') + S("\\r"); + else + S("\\x%02x", c); + } else + S("%c", c); + } + S("'\n"); + } + + n = sys_channel_write( call->channel, + call->buff + call->buff_pos, + call->buff_len - call->buff_pos ); + if (n <= 0) { + /* remote emulator probably quitted */ + S("%s: emulator %d quitted unexpectedly with error %d: %s\n", + __FUNCTION__, call->to_port, errno, errno_str); + if (call->result_func) + call->result_func( call->result_opaque, 0 ); + remote_call_free( call ); + return; + } + call->buff_pos += n; + + if (call->buff_pos >= call->buff_len) { + /* cool, we sent everything */ + S("%s: finished sending data to %d\n", __FUNCTION__, call->to_port); + if (!call->quitting) { + call->quitting = 1; + sprintf( call->buff, "quit\n" ); + call->buff_len = strlen(call->buff); + call->buff_pos = 0; + } else { + call->quitting = 0; + if (call->result_func) + call->result_func( call->result_opaque, 1 ); + + sys_channel_on( call->channel, SYS_EVENT_READ, remote_call_event, call ); + } + } + } +} + +static RemoteCall _the_remote_calls; + +#if 0 +static int +remote_from_number( const char* from ) +{ + char* end; + long num = strtol( from, &end, 10 ); + + if (end == NULL || *end) + return -1; + + if ((unsigned)(num - REMOTE_NUMBER_BASE) >= REMOTE_NUMBER_MAX) + return -1; + + return (int) num; +} +#endif + +static RemoteCall +remote_call_generic( RemoteCallType type, const char* to_number, int from_port ) +{ + int to_port = remote_number_string_to_port(to_number); + RemoteCall call; + + if ( remote_number_from_port(from_port) < 0 ) { + D("%s: from_port value %d is not valid", __FUNCTION__, from_port); + return NULL; + } + if ( to_port < 0 ) { + D("%s: phone number '%s' is not decimal or remote", __FUNCTION__, to_number); + return NULL; + } + if (to_port == from_port) { + D("%s: trying to call self\n", __FUNCTION__); + return NULL; + } + call = remote_call_alloc( type, to_port, from_port ); + if (call == NULL) { + return NULL; + } + remote_call_add( call, &_the_remote_calls ); + D("%s: adding new call from port %d to port %d\n", __FUNCTION__, from_port, to_port); + return call; +} + + +int +remote_call_dial( const char* number, + int from, + RemoteResultFunc result_func, + void* result_opaque ) +{ + RemoteCall call = remote_call_generic( REMOTE_CALL_DIAL, number, from ); + + if (call != NULL) { + call->result_func = result_func; + call->result_opaque = result_opaque; + } + return call ? 0 : -1; +} + + +void +remote_call_other( const char* to_number, int from_port, RemoteCallType type ) +{ + remote_call_generic( type, to_number, from_port ); +} + +/* call this function to send a SMS to a remote emulator */ +int +remote_call_sms( const char* number, + int from, + SmsPDU pdu ) +{ + RemoteCall call = remote_call_generic( REMOTE_CALL_SMS, number, from ); + + if (call == NULL) + return -1; + + if (call != NULL) { + if ( remote_call_set_sms_pdu( call, pdu ) < 0 ) { + remote_call_free(call); + return -1; + } + } + return call ? 0 : -1; +} + + +void +remote_call_cancel( const char* to_number, int from_port ) +{ + remote_call_generic( REMOTE_CALL_HANGUP, to_number, from_port ); +} |