| /* 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 "android/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; |
| const char* temp = number; |
| int len; |
| |
| len = strlen(number); |
| if (len > 0 && number[len-1] == ';') |
| len--; |
| if (len == 11 && !memcmp(number, PHONE_PREFIX, 7)) |
| temp += 7; |
| num = strtol( temp, &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 " PHONE_PREFIX "%d\n", from_num ); |
| break; |
| |
| case REMOTE_CALL_BUSY: |
| p = bufprint(p, end, "gsm busy " PHONE_PREFIX "%d\n", from_num); |
| break; |
| |
| case REMOTE_CALL_HOLD: |
| p = bufprint(p, end, "gsm hold " PHONE_PREFIX "%d\n", from_num); |
| break; |
| |
| case REMOTE_CALL_ACCEPT: |
| p = bufprint(p, end, "gsm accept " PHONE_PREFIX "%d\n", from_num); |
| break; |
| |
| case REMOTE_CALL_HANGUP: |
| p = bufprint(p, end, "gsm cancel " PHONE_PREFIX "%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 ); |
| } |