blob: f913f7d6bcd60ed3be1b12133c4a371b2ca116c2 [file] [log] [blame]
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001/* Copyright (C) 2007-2008 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10** GNU General Public License for more details.
11*/
12/*
13 * Android emulator control console
14 *
15 * this console is enabled automatically at emulator startup, on port 5554 by default,
16 * unless some other emulator is already running. See (android_emulation_start in android_sdl.c
17 * for details)
18 *
19 * you can telnet to the console, then use commands like 'help' or others to dynamically
20 * change emulator settings.
21 *
22 */
23
24#include "sockets.h"
25#include "qemu-char.h"
26#include "sysemu.h"
27#include "android/android.h"
28#include "sockets.h"
29#include "cpu.h"
30#include "hw/goldfish_device.h"
31#include "hw/power_supply.h"
32#include "shaper.h"
33#include "modem_driver.h"
34#include "android/gps.h"
35#include "android/globals.h"
36#include "android/utils/bufprint.h"
37#include "android/utils/debug.h"
38#include "android/utils/stralloc.h"
39#include "tcpdump.h"
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070040#include "net.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080041
42#include <stdlib.h>
43#include <stdio.h>
44#include <stdarg.h>
45#include <string.h>
46#include <unistd.h>
47#include <fcntl.h>
48#include "android/hw-events.h"
David 'Digit' Turner34f29742010-05-25 18:16:10 -070049#include "user-events.h"
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -070050#include "android/keycode-array.h"
51#include "android/charmap.h"
Vladimir Chtchetkine2fa51732010-07-16 11:19:48 -070052#include "android/core-ui-protocol.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080053
54#if defined(CONFIG_SLIRP)
55#include "libslirp.h"
56#endif
57
58/* set to 1 to use the i/o and event functions
59 * defined in "telephony/sysdeps.h"
60 */
61#define USE_SYSDEPS 0
62
63#include "sysdeps.h"
64
65#define DEBUG 1
66
67#if 1
68# define D_ACTIVE VERBOSE_CHECK(console)
69#else
70# define D_ACTIVE DEBUG
71#endif
72
73#if DEBUG
74# define D(x) do { if (D_ACTIVE) ( printf x , fflush(stdout) ); } while (0)
75#else
76# define D(x) do{}while(0)
77#endif
78
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080079typedef struct ControlGlobalRec_* ControlGlobal;
80
81typedef struct ControlClientRec_* ControlClient;
82
83typedef struct {
84 int host_port;
85 int host_udp;
86 unsigned int guest_ip;
87 int guest_port;
88} RedirRec, *Redir;
89
90
91#if USE_SYSDEPS
92typedef SysChannel Socket;
93#else /* !USE_SYSDEPS */
94typedef int Socket;
95#endif /* !USE_SYSDEPS */
96
97
98typedef struct ControlClientRec_
99{
100 struct ControlClientRec_* next; /* next client in list */
101 Socket sock; /* socket used for communication */
102 ControlGlobal global;
103 char finished;
104 char buff[ 4096 ];
105 int buff_len;
106
107} ControlClientRec;
108
109
110typedef struct ControlGlobalRec_
111{
112 /* listening socket */
113 Socket listen_fd;
114
115 /* the list of current clients */
116 ControlClient clients;
117
118 /* the list of redirections currently active */
119 Redir redirs;
120 int num_redirs;
121 int max_redirs;
122
123} ControlGlobalRec;
124
125
126static int
127control_global_add_redir( ControlGlobal global,
128 int host_port,
129 int host_udp,
130 unsigned int guest_ip,
131 int guest_port )
132{
133 Redir redir;
134
135 if (global->num_redirs >= global->max_redirs)
136 {
137 int old_max = global->max_redirs;
138 int new_max = old_max + (old_max >> 1) + 4;
139
140 Redir new_redirs = realloc( global->redirs, new_max*sizeof(global->redirs[0]) );
141 if (new_redirs == NULL)
142 return -1;
143
144 global->redirs = new_redirs;
145 global->max_redirs = new_max;
146 }
147
148 redir = &global->redirs[ global->num_redirs++ ];
149
150 redir->host_port = host_port;
151 redir->host_udp = host_udp;
152 redir->guest_ip = guest_ip;
153 redir->guest_port = guest_port;
154
155 return 0;
156}
157
158static int
159control_global_del_redir( ControlGlobal global,
160 int host_port,
161 int host_udp )
162{
163 int nn;
164
165 for (nn = 0; nn < global->num_redirs; nn++)
166 {
167 Redir redir = &global->redirs[nn];
168
169 if ( redir->host_port == host_port &&
170 redir->host_udp == host_udp )
171 {
172 memmove( redir, redir + 1, ((global->num_redirs - nn)-1)*sizeof(*redir) );
173 global->num_redirs -= 1;
174 return 0;
175 }
176 }
177 /* we didn't find it */
178 return -1;
179}
180
181static void
182control_client_destroy( ControlClient client )
183{
184 ControlGlobal global = client->global;
185 ControlClient *pnode = &global->clients;
186
187 D(( "destroying control client %p\n", client ));
188
189#if USE_SYSDEPS
190 sys_channel_on( client->sock, 0, NULL, NULL );
191#else
192 qemu_set_fd_handler( client->sock, NULL, NULL, NULL );
193#endif
194
195 for ( ;; ) {
196 ControlClient node = *pnode;
197 if ( node == NULL )
198 break;
199 if ( node == client ) {
200 *pnode = node->next;
201 node->next = NULL;
202 break;
203 }
204 pnode = &node->next;
205 }
206
207#if USE_SYSDEPS
208 sys_channel_close( client->sock );
209 client->sock = NULL;
210#else
211 socket_close( client->sock );
212 client->sock = -1;
213#endif
214
215 free( client );
216}
217
218static void control_client_read( void* _client ); /* forward */
219
220
221static void control_control_write( ControlClient client, const char* buff, int len )
222{
223 int ret;
224
225 if (len < 0)
226 len = strlen(buff);
227
228 while (len > 0) {
229#if USE_SYSDEPS
230 ret = sys_channel_write( client->sock, buff, len );
231#else
232 ret = socket_send( client->sock, buff, len);
233#endif
234 if (ret < 0) {
David 'Digit' Turnerce0f4b02010-03-25 11:11:29 -0700235 if (errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800236 return;
237 } else {
238 buff += ret;
239 len -= ret;
240 }
241 }
242}
243
244static void control_write( ControlClient client, const char* format, ... )
245{
246 static char temp[1024];
247 va_list args;
248
249 va_start(args, format);
250 vsnprintf( temp, sizeof(temp), format, args );
251 va_end(args);
252
253 temp[ sizeof(temp)-1 ] = 0;
254
255 control_control_write( client, temp, -1 );
256}
257
258
259static ControlClient
260control_client_create( Socket socket,
261 ControlGlobal global )
262{
263 ControlClient client = calloc( sizeof(*client), 1 );
264
265 if (client) {
266#if !USE_SYSDEPS
267 socket_set_nodelay( socket );
268 socket_set_nonblock( socket );
269#endif
270 client->finished = 0;
271 client->global = global;
272 client->sock = socket;
273 client->next = global->clients;
274 global->clients = client;
275
276#if USE_SYSDEPS
277 sys_channel_on( socket, SYS_EVENT_READ,
278 (SysChannelCallback) control_client_read,
279 client );
280#else
281 qemu_set_fd_handler( socket, control_client_read, NULL, client );
282#endif
283 }
284 return client;
285}
286
287typedef const struct CommandDefRec_ *CommandDef;
288
289typedef struct CommandDefRec_ {
290 const char* names;
291 const char* abstract;
292 const char* description;
293 void (*descriptor)( ControlClient client );
294 int (*handler)( ControlClient client, char* args );
295 CommandDef subcommands; /* if handler is NULL */
296
297} CommandDefRec;
298
299static const CommandDefRec main_commands[]; /* forward */
300
301static CommandDef
302find_command( char* input, CommandDef commands, char* *pend, char* *pargs )
303{
304 int nn;
305 char* args = strchr(input, ' ');
306
307 if (args != NULL) {
308 while (*args == ' ')
309 args++;
310
311 if (args[0] == 0)
312 args = NULL;
313 }
314
315 for (nn = 0; commands[nn].names != NULL; nn++)
316 {
317 const char* name = commands[nn].names;
318 const char* sep;
319
320 do {
321 int len, c;
322
323 sep = strchr( name, '|' );
324 if (sep)
325 len = sep - name;
326 else
327 len = strlen(name);
328
329 c = input[len];
330 if ( !memcmp( name, input, len ) && (c == ' ' || c == 0) ) {
331 *pend = input + len;
332 *pargs = args;
333 return &commands[nn];
334 }
335
336 if (sep)
337 name = sep + 1;
338
339 } while (sep != NULL && *name);
340 }
341 /* NOTE: don't touch *pend and *pargs if no command is found */
342 return NULL;
343}
344
345static void
346dump_help( ControlClient client,
347 CommandDef cmd,
348 const char* prefix )
349{
350 if (cmd->description) {
351 control_write( client, "%s", cmd->description );
352 } else if (cmd->descriptor) {
353 cmd->descriptor( client );
354 } else
355 control_write( client, "%s\r\n", cmd->abstract );
356
357 if (cmd->subcommands) {
358 cmd = cmd->subcommands;
359 control_write( client, "\r\navailable sub-commands:\r\n" );
360 for ( ; cmd->names != NULL; cmd++ ) {
361 control_write( client, " %s %-15s %s\r\n", prefix, cmd->names, cmd->abstract );
362 }
363 control_write( client, "\r\n" );
364 }
365}
366
367static void
368control_client_do_command( ControlClient client )
369{
370 char* line = client->buff;
371 char* args = NULL;
372 CommandDef commands = main_commands;
373 char* cmdend = client->buff;
374 CommandDef cmd = find_command( line, commands, &cmdend, &args );
375
376 if (cmd == NULL) {
377 control_write( client, "KO: unknown command, try 'help'\r\n" );
378 return;
379 }
380
381 for (;;) {
382 CommandDef subcmd;
383
384 if (cmd->handler) {
385 if ( !cmd->handler( client, args ) )
386 control_write( client, "OK\r\n" );
387 break;
388 }
389
390 /* no handler means we should have sub-commands */
391 if (cmd->subcommands == NULL) {
392 control_write( client, "KO: internal error: buggy command table for '%.*s'\r\n",
393 cmdend - client->buff, client->buff );
394 break;
395 }
396
397 /* we need a sub-command here */
398 if ( !args ) {
399 dump_help( client, cmd, "" );
400 control_write( client, "KO: missing sub-command\r\n" );
401 break;
402 }
403
404 line = args;
405 commands = cmd->subcommands;
406 subcmd = find_command( line, commands, &cmdend, &args );
407 if (subcmd == NULL) {
408 dump_help( client, cmd, "" );
409 control_write( client, "KO: bad sub-command\r\n" );
410 break;
411 }
412 cmd = subcmd;
413 }
414}
415
416/* implement the 'help' command */
417static int
418do_help( ControlClient client, char* args )
419{
420 char* line;
421 char* start = args;
422 char* end = start;
423 CommandDef cmd = main_commands;
424
425 /* without arguments, simply dump all commands */
426 if (args == NULL) {
427 control_write( client, "Android console command help:\r\n\r\n" );
428 for ( ; cmd->names != NULL; cmd++ ) {
429 control_write( client, " %-15s %s\r\n", cmd->names, cmd->abstract );
430 }
431 control_write( client, "\r\ntry 'help <command>' for command-specific help\r\n" );
432 return 0;
433 }
434
435 /* with an argument, find the corresponding command */
436 for (;;) {
437 CommandDef subcmd;
438
439 line = args;
440 subcmd = find_command( line, cmd, &end, &args );
441 if (subcmd == NULL) {
442 control_write( client, "try one of these instead:\r\n\r\n" );
443 for ( ; cmd->names != NULL; cmd++ ) {
444 control_write( client, " %.*s %s\r\n",
445 end - start, start, cmd->names );
446 }
447 control_write( client, "\r\nKO: unknown command\r\n" );
448 return -1;
449 }
450
451 if ( !args || !subcmd->subcommands ) {
452 dump_help( client, subcmd, start );
453 return 0;
454 }
455 cmd = subcmd->subcommands;
456 }
457}
458
459
460static void
461control_client_read_byte( ControlClient client, unsigned char ch )
462{
463 if (ch == '\r')
464 {
465 /* filter them out */
466 }
467 else if (ch == '\n')
468 {
469 client->buff[ client->buff_len ] = 0;
470 control_client_do_command( client );
471 if (client->finished)
472 return;
473
474 client->buff_len = 0;
475 }
476 else
477 {
478 if (client->buff_len >= sizeof(client->buff)-1)
479 client->buff_len = 0;
480
481 client->buff[ client->buff_len++ ] = ch;
482 }
483}
484
485static void
486control_client_read( void* _client )
487{
488 ControlClient client = _client;
489 unsigned char buf[4096];
490 int size;
491
492 D(( "in control_client read: " ));
493#if USE_SYSDEPS
494 size = sys_channel_read( client->sock, buf, sizeof(buf) );
495#else
496 size = socket_recv( client->sock, buf, sizeof(buf) );
497#endif
498 if (size < 0) {
499 D(( "size < 0, exiting with %d: %s\n", errno, errno_str ));
David 'Digit' Turnerce0f4b02010-03-25 11:11:29 -0700500 if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800501 control_client_destroy( client );
502 return;
503 }
504
505 if (size == 0) {
506 /* end of connection */
507 D(( "end of connection detected !!\n" ));
508 control_client_destroy( client );
509 }
510 else {
511 int nn;
512#ifdef _WIN32
513# if DEBUG
514 char temp[16];
515 int count = size > sizeof(temp)-1 ? sizeof(temp)-1 : size;
516 for (nn = 0; nn < count; nn++) {
517 int c = buf[nn];
518 if (c == '\n')
519 temp[nn] = '!';
520 else if (c < 32)
521 temp[nn] = '.';
522 else
523 temp[nn] = (char)c;
524 }
525 temp[nn] = 0;
526 D(( "received %d bytes: %s\n", size, temp ));
527# endif
528#else
529 D(( "received %.*s\n", size, buf ));
530#endif
531 for (nn = 0; nn < size; nn++) {
532 control_client_read_byte( client, buf[nn] );
533 if (client->finished) {
534 control_client_destroy(client);
535 return;
536 }
537 }
538 }
539}
540
541
542/* this function is called on each new client connection */
543static void
544control_global_accept( void* _global )
545{
546 ControlGlobal global = _global;
547 ControlClient client;
548 Socket fd;
549
550 D(( "control_global_accept: just in (fd=%p)\n", (void*)global->listen_fd ));
551
552#if USE_SYSDEPS
553 fd = sys_channel_create_tcp_handler( global->listen_fd );
554 if (fd == NULL) {
555 perror("accept");
556 return;
557 }
558#else
559 for(;;) {
560 fd = socket_accept( global->listen_fd, NULL );
561 if (fd < 0 && errno != EINTR) {
562 D(( "problem in accept: %d: %s\n", errno, errno_str ));
563 perror("accept");
564 return;
565 } else if (fd >= 0) {
566 break;
567 }
568 D(( "relooping in accept()\n" ));
569 }
570
571 socket_set_xreuseaddr( fd );
572#endif
573
574 D(( "control_global_accept: creating new client\n" ));
575 client = control_client_create( fd, global );
576 if (client) {
577 D(( "control_global_accept: new client %p\n", client ));
578 control_write( client, "Android Console: type 'help' for a list of commands\r\n" );
579 control_write( client, "OK\r\n" );
580 }
581}
582
583
584static int
585control_global_init( ControlGlobal global,
586 int control_port )
587{
588 Socket fd;
589#if !USE_SYSDEPS
590 int ret;
591 SockAddress sockaddr;
592#endif
593
594 memset( global, 0, sizeof(*global) );
595
596 sys_main_init();
597
598#if USE_SYSDEPS
599 fd = sys_channel_create_tcp_server( control_port );
600 if (fd == NULL) {
601 return -1;
602 }
603
604 D(("global fd=%p\n", fd));
605
606 global->listen_fd = fd;
607 sys_channel_on( fd, SYS_EVENT_READ,
608 (SysChannelCallback) control_global_accept,
609 global );
610#else
611 fd = socket_create_inet( SOCKET_STREAM );
612 if (fd < 0) {
613 perror("socket");
614 return -1;
615 }
616
617 socket_set_xreuseaddr( fd );
618
619 sock_address_init_inet( &sockaddr, SOCK_ADDRESS_INET_LOOPBACK, control_port );
620
621 ret = socket_bind(fd, &sockaddr );
622 if (ret < 0) {
623 perror("bind");
624 socket_close( fd );
625 return -1;
626 }
627
628 ret = socket_listen(fd, 0);
629 if (ret < 0) {
630 perror("listen");
631 socket_close( fd );
632 return -1;
633 }
634
635 socket_set_nonblock(fd);
636
637 global->listen_fd = fd;
638
639 qemu_set_fd_handler( fd, control_global_accept, NULL, global );
640#endif
641 return 0;
642}
643
644
645
646static int
647do_quit( ControlClient client, char* args )
648{
649 client->finished = 1;
650 return -1;
651}
652
653/********************************************************************************************/
654/********************************************************************************************/
655/***** ******/
656/***** N E T W O R K S E T T I N G S ******/
657/***** ******/
658/********************************************************************************************/
659/********************************************************************************************/
660
661static int
662do_network_status( ControlClient client, char* args )
663{
664 control_write( client, "Current network status:\r\n" );
665
666 control_write( client, " download speed: %8d bits/s (%.1f KB/s)\r\n",
667 (long)qemu_net_download_speed, qemu_net_download_speed/8192. );
668
669 control_write( client, " upload speed: %8d bits/s (%.1f KB/s)\r\n",
670 (long)qemu_net_upload_speed, qemu_net_upload_speed/8192. );
671
672 control_write( client, " minimum latency: %ld ms\r\n", qemu_net_min_latency );
673 control_write( client, " maximum latency: %ld ms\r\n", qemu_net_max_latency );
674 return 0;
675}
676
677static void
678dump_network_speeds( ControlClient client )
679{
680 const NetworkSpeed* speed = android_netspeeds;
681 const char* const format = " %-8s %s\r\n";
682 for ( ; speed->name; speed++ ) {
683 control_write( client, format, speed->name, speed->display );
684 }
685 control_write( client, format, "<num>", "selects both upload and download speed" );
686 control_write( client, format, "<up>:<down>", "select individual upload/download speeds" );
687}
688
689
690static int
691do_network_speed( ControlClient client, char* args )
692{
693 if ( !args ) {
694 control_write( client, "KO: missing <speed> argument, see 'help network speed'\r\n" );
695 return -1;
696 }
697 if ( android_parse_network_speed( args ) < 0 ) {
698 control_write( client, "KO: invalid <speed> argument, see 'help network speed' for valid values\r\n" );
699 return -1;
700 }
701
702 netshaper_set_rate( slirp_shaper_in, qemu_net_download_speed );
703 netshaper_set_rate( slirp_shaper_out, qemu_net_upload_speed );
704
705 if (android_modem) {
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -0700706 amodem_set_data_network_type( android_modem,
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800707 android_parse_network_type( args ) );
708 }
709 return 0;
710}
711
712static void
713describe_network_speed( ControlClient client )
714{
715 control_write( client,
716 "'network speed <speed>' allows you to dynamically change the speed of the emulated\r\n"
717 "network on the device, where <speed> is one of the following:\r\n\r\n" );
718 dump_network_speeds( client );
719}
720
721static int
722do_network_delay( ControlClient client, char* args )
723{
724 if ( !args ) {
725 control_write( client, "KO: missing <delay> argument, see 'help network delay'\r\n" );
726 return -1;
727 }
728 if ( android_parse_network_latency( args ) < 0 ) {
729 control_write( client, "KO: invalid <delay> argument, see 'help network delay' for valid values\r\n" );
730 return -1;
731 }
732 netdelay_set_latency( slirp_delay_in, qemu_net_min_latency, qemu_net_max_latency );
733 return 0;
734}
735
736static void
737describe_network_delay( ControlClient client )
738{
739 control_write( client,
740 "'network delay <latency>' allows you to dynamically change the latency of the emulated\r\n"
741 "network on the device, where <latency> is one of the following:\r\n\r\n" );
742 /* XXX: TODO */
743}
744
745static int
746do_network_capture_start( ControlClient client, char* args )
747{
748 if ( !args ) {
749 control_write( client, "KO: missing <file> argument, see 'help network capture start'\r\n" );
750 return -1;
751 }
752 if ( qemu_tcpdump_start(args) < 0) {
753 control_write( client, "KO: could not start capture: %s", strerror(errno) );
754 return -1;
755 }
756 return 0;
757}
758
759static int
760do_network_capture_stop( ControlClient client, char* args )
761{
762 /* no need to return an error here */
763 qemu_tcpdump_stop();
764 return 0;
765}
766
767static const CommandDefRec network_capture_commands[] =
768{
769 { "start", "start network capture",
770 "'network capture start <file>' starts a new capture of network packets\r\n"
771 "into a specific <file>. This will stop any capture already in progress.\r\n"
772 "the capture file can later be analyzed by tools like WireShark. It uses\r\n"
773 "the libpcap file format.\r\n\r\n"
774 "you can stop the capture anytime with 'network capture stop'\r\n", NULL,
775 do_network_capture_start, NULL },
776
777 { "stop", "stop network capture",
778 "'network capture stop' stops a currently running packet capture, if any.\r\n"
779 "you can start one with 'network capture start <file>'\r\n", NULL,
780 do_network_capture_stop, NULL },
781
782 { NULL, NULL, NULL, NULL, NULL, NULL }
783};
784
785static const CommandDefRec network_commands[] =
786{
787 { "status", "dump network status", NULL, NULL,
788 do_network_status, NULL },
789
790 { "speed", "change network speed", NULL, describe_network_speed,
791 do_network_speed, NULL },
792
793 { "delay", "change network latency", NULL, describe_network_delay,
794 do_network_delay, NULL },
795
796 { "capture", "dump network packets to file",
797 "allows to start/stop capture of network packets to a file for later analysis\r\n", NULL,
798 NULL, network_capture_commands },
799
800 { NULL, NULL, NULL, NULL, NULL, NULL }
801};
802
803/********************************************************************************************/
804/********************************************************************************************/
805/***** ******/
806/***** P O R T R E D I R E C T I O N S ******/
807/***** ******/
808/********************************************************************************************/
809/********************************************************************************************/
810
811static int
812do_redir_list( ControlClient client, char* args )
813{
814 ControlGlobal global = client->global;
815
816 if (global->num_redirs == 0)
817 control_write( client, "no active redirections\r\n" );
818 else {
819 int nn;
820 for (nn = 0; nn < global->num_redirs; nn++) {
821 Redir redir = &global->redirs[nn];
822 control_write( client, "%s:%-5d => %-5d\r\n",
823 redir->host_udp ? "udp" : "tcp",
824 redir->host_port,
825 redir->guest_port );
826 }
827 }
828 return 0;
829}
830
831/* parse a protocol:port specification */
832static int
833redir_parse_proto_port( char* args, int *pport, int *pproto )
834{
835 int proto = -1;
836 int len = 0;
837 char* end;
838
839 if ( !memcmp( args, "tcp:", 4 ) ) {
840 proto = 0;
841 len = 4;
842 }
843 else if ( !memcmp( args, "udp:", 4 ) ) {
844 proto = 1;
845 len = 4;
846 }
847 else
848 return 0;
849
850 args += len;
851 *pproto = proto;
852 *pport = strtol( args, &end, 10 );
853 if (end == args)
854 return 0;
855
856 len += end - args;
857 return len;
858}
859
860static int
861redir_parse_guest_port( char* arg, int *pport )
862{
863 char* end;
864
865 *pport = strtoul( arg, &end, 10 );
866 if (end == arg)
867 return 0;
868
869 return end - arg;
870}
871
872static Redir
873redir_find( ControlGlobal global, int port, int isudp )
874{
875 int nn;
876
877 for (nn = 0; nn < global->num_redirs; nn++) {
878 Redir redir = &global->redirs[nn];
879
880 if (redir->host_port == port && redir->host_udp == isudp)
881 return redir;
882 }
883 return NULL;
884}
885
886
887static int
888do_redir_add( ControlClient client, char* args )
889{
890 int len, host_proto, host_port, guest_port;
891 uint32_t guest_ip;
892 Redir redir;
893
894 if ( !args )
895 goto BadFormat;
896
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700897 if (!slirp_is_inited()) {
898 control_write( client, "KO: network emulation disabled\r\n");
899 return -1;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800900 }
901
902 len = redir_parse_proto_port( args, &host_port, &host_proto );
903 if (len == 0 || args[len] != ':')
904 goto BadFormat;
905
906 args += len + 1;
907 len = redir_parse_guest_port( args, &guest_port );
908 if (len == 0 || args[len] != 0)
909 goto BadFormat;
910
911 redir = redir_find( client->global, host_port, host_proto );
912 if ( redir != NULL ) {
913 control_write( client, "KO: host port already active, use 'redir del' to remove first\r\n" );
914 return -1;
915 }
916
David Turner7d9a2702009-04-14 14:43:24 -0700917 if (inet_strtoip("10.0.2.15", &guest_ip) < 0) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800918 control_write( client, "KO: unexpected internal failure when resolving 10.0.2.15\r\n" );
919 return -1;
920 }
921
922 D(("pattern hport=%d gport=%d proto=%d\n", host_port, guest_port, host_proto ));
923 if ( control_global_add_redir( client->global, host_port, host_proto,
924 guest_ip, guest_port ) < 0 )
925 {
926 control_write( client, "KO: not enough memory to allocate redirection\r\n" );
927 return -1;
928 }
929
930 if (slirp_redir(host_proto, host_port, guest_ip, guest_port) < 0) {
931 control_write( client, "KO: can't setup redirection, port probably used by another program on host\r\n" );
932 control_global_del_redir( client->global, host_port, host_proto );
933 return -1;
934 }
935
936 return 0;
937
938BadFormat:
939 control_write( client, "KO: bad redirection format, try (tcp|udp):hostport:guestport\r\n", -1 );
940 return -1;
941}
942
943
944static int
945do_redir_del( ControlClient client, char* args )
946{
947 int len, proto, port;
948 Redir redir;
949
950 if ( !args )
951 goto BadFormat;
952 len = redir_parse_proto_port( args, &port, &proto );
953 if ( len == 0 || args[len] != 0 )
954 goto BadFormat;
955
956 redir = redir_find( client->global, port, proto );
957 if (redir == NULL) {
958 control_write( client, "KO: can't remove unknown redirection (%s:%d)\r\n",
959 proto ? "udp" : "tcp", port );
960 return -1;
961 }
962
963 slirp_unredir( redir->host_udp, redir->host_port );
964 control_global_del_redir( client->global, port, proto );\
965
966 return 0;
967
968BadFormat:
969 control_write( client, "KO: bad redirection format, try (tcp|udp):hostport\r\n" );
970 return -1;
971}
972
973static const CommandDefRec redir_commands[] =
974{
975 { "list", "list current redirections",
976 "list current port redirections. use 'redir add' and 'redir del' to add and remove them\r\n", NULL,
977 do_redir_list, NULL },
978
979 { "add", "add new redirection",
980 "add a new port redirection, arguments must be:\r\n\r\n"
981 " redir add <protocol>:<host-port>:<guest-port>\r\n\r\n"
982 "where: <protocol> is either 'tcp' or 'udp'\r\n"
983 " <host-port> a number indicating which port on the host to open\r\n"
984 " <guest-port> a number indicating which port to route to on the device\r\n"
985 "\r\nas an example, 'redir tcp:5000:6000' will allow any packets sent to\r\n"
986 "the host's TCP port 5000 to be routed to TCP port 6000 of the emulated device\r\n", NULL,
987 do_redir_add, NULL },
988
989 { "del", "remove existing redirection",
990 "remove a port redirecion that was created with 'redir add', arguments must be:\r\n\r\n"
991 " redir del <protocol>:<host-port>\r\n\r\n"
992 "see the 'help redir add' for the meaning of <protocol> and <host-port>\r\n", NULL,
993 do_redir_del, NULL },
994
995 { NULL, NULL, NULL, NULL, NULL, NULL }
996};
997
998
999
1000/********************************************************************************************/
1001/********************************************************************************************/
1002/***** ******/
1003/***** G S M M O D E M ******/
1004/***** ******/
1005/********************************************************************************************/
1006/********************************************************************************************/
1007
1008static const struct {
1009 const char* name;
1010 const char* display;
1011 ARegistrationState state;
1012} _gsm_states[] = {
1013 { "unregistered", "no network available", A_REGISTRATION_UNREGISTERED },
1014 { "home", "on local network, non-roaming", A_REGISTRATION_HOME },
1015 { "roaming", "on roaming network", A_REGISTRATION_ROAMING },
1016 { "searching", "searching networks", A_REGISTRATION_SEARCHING },
1017 { "denied", "emergency calls only", A_REGISTRATION_DENIED },
1018 { "off", "same as 'unregistered'", A_REGISTRATION_UNREGISTERED },
1019 { "on", "same as 'home'", A_REGISTRATION_HOME },
1020 { NULL, NULL, A_REGISTRATION_UNREGISTERED }
1021};
1022
1023static const char*
1024gsm_state_to_string( ARegistrationState state )
1025{
1026 int nn;
1027 for (nn = 0; _gsm_states[nn].name != NULL; nn++) {
1028 if (state == _gsm_states[nn].state)
1029 return _gsm_states[nn].name;
1030 }
1031 return "<unknown>";
1032}
1033
1034static int
1035do_gsm_status( ControlClient client, char* args )
1036{
1037 if (args) {
1038 control_write( client, "KO: no argument required\r\n" );
1039 return -1;
1040 }
1041 if (!android_modem) {
1042 control_write( client, "KO: modem emulation not running\r\n" );
1043 return -1;
1044 }
1045 control_write( client, "gsm voice state: %s\r\n",
1046 gsm_state_to_string(
1047 amodem_get_voice_registration(android_modem) ) );
1048 control_write( client, "gsm data state: %s\r\n",
1049 gsm_state_to_string(
1050 amodem_get_data_registration(android_modem) ) );
1051 return 0;
1052}
1053
1054
1055static void
1056help_gsm_data( ControlClient client )
1057{
1058 int nn;
1059 control_write( client,
1060 "the 'gsm data <state>' allows you to change the state of your GPRS connection\r\n"
1061 "valid values for <state> are the following:\r\n\r\n" );
1062 for (nn = 0; ; nn++) {
1063 const char* name = _gsm_states[nn].name;
1064 const char* display = _gsm_states[nn].display;
1065
1066 if (!name)
1067 break;
1068
1069 control_write( client, " %-15s %s\r\n", name, display );
1070 }
1071 control_write( client, "\r\n" );
1072}
1073
1074
1075static int
1076do_gsm_data( ControlClient client, char* args )
1077{
1078 int nn;
1079
1080 if (!args) {
1081 control_write( client, "KO: missing argument, try 'gsm data <state>'\r\n" );
1082 return -1;
1083 }
1084
1085 for (nn = 0; ; nn++) {
1086 const char* name = _gsm_states[nn].name;
1087 ARegistrationState state = _gsm_states[nn].state;
1088
1089 if (!name)
1090 break;
1091
1092 if ( !strcmp( args, name ) ) {
1093 if (!android_modem) {
1094 control_write( client, "KO: modem emulation not running\r\n" );
1095 return -1;
1096 }
1097 amodem_set_data_registration( android_modem, state );
1098 qemu_net_disable = (state != A_REGISTRATION_HOME &&
1099 state != A_REGISTRATION_ROAMING );
1100 return 0;
1101 }
1102 }
1103 control_write( client, "KO: bad GSM data state name, try 'help gsm data' for list of valid values\r\n" );
1104 return -1;
1105}
1106
1107static void
1108help_gsm_voice( ControlClient client )
1109{
1110 int nn;
1111 control_write( client,
1112 "the 'gsm voice <state>' allows you to change the state of your GPRS connection\r\n"
1113 "valid values for <state> are the following:\r\n\r\n" );
1114 for (nn = 0; ; nn++) {
1115 const char* name = _gsm_states[nn].name;
1116 const char* display = _gsm_states[nn].display;
1117
1118 if (!name)
1119 break;
1120
1121 control_write( client, " %-15s %s\r\n", name, display );
1122 }
1123 control_write( client, "\r\n" );
1124}
1125
1126
1127static int
1128do_gsm_voice( ControlClient client, char* args )
1129{
1130 int nn;
1131
1132 if (!args) {
1133 control_write( client, "KO: missing argument, try 'gsm voice <state>'\r\n" );
1134 return -1;
1135 }
1136
1137 for (nn = 0; ; nn++) {
1138 const char* name = _gsm_states[nn].name;
1139 ARegistrationState state = _gsm_states[nn].state;
1140
1141 if (!name)
1142 break;
1143
1144 if ( !strcmp( args, name ) ) {
1145 if (!android_modem) {
1146 control_write( client, "KO: modem emulation not running\r\n" );
1147 return -1;
1148 }
1149 amodem_set_voice_registration( android_modem, state );
1150 return 0;
1151 }
1152 }
1153 control_write( client, "KO: bad GSM data state name, try 'help gsm voice' for list of valid values\r\n" );
1154 return -1;
1155}
1156
1157
1158static int
1159gsm_check_number( char* args )
1160{
1161 int nn;
1162
1163 for (nn = 0; args[nn] != 0; nn++) {
1164 int c = args[nn];
1165 if ( !isdigit(c) && c != '+' && c != '#' ) {
1166 return -1;
1167 }
1168 }
1169 if (nn == 0)
1170 return -1;
1171
1172 return 0;
1173}
1174
1175static int
1176do_gsm_call( ControlClient client, char* args )
1177{
1178 /* check that we have a phone number made of digits */
1179 if (!args) {
1180 control_write( client, "KO: missing argument, try 'gsm call <phonenumber>'\r\n" );
1181 return -1;
1182 }
1183
1184 if (gsm_check_number(args)) {
1185 control_write( client, "KO: bad phone number format, use digits, # and + only\r\n" );
1186 return -1;
1187 }
1188
1189 if (!android_modem) {
1190 control_write( client, "KO: modem emulation not running\r\n" );
1191 return -1;
1192 }
1193 amodem_add_inbound_call( android_modem, args );
1194 return 0;
1195}
1196
1197static int
1198do_gsm_cancel( ControlClient client, char* args )
1199{
1200 if (!args) {
1201 control_write( client, "KO: missing argument, try 'gsm call <phonenumber>'\r\n" );
1202 return -1;
1203 }
1204 if (gsm_check_number(args)) {
1205 control_write( client, "KO: bad phone number format, use digits, # and + only\r\n" );
1206 return -1;
1207 }
1208 if (!android_modem) {
1209 control_write( client, "KO: modem emulation not running\r\n" );
1210 return -1;
1211 }
1212 if ( amodem_disconnect_call( android_modem, args ) < 0 ) {
1213 control_write( client, "KO: could not cancel this number\r\n" );
1214 return -1;
1215 }
1216 return 0;
1217}
1218
1219
1220static const char*
1221call_state_to_string( ACallState state )
1222{
1223 switch (state) {
1224 case A_CALL_ACTIVE: return "active";
1225 case A_CALL_HELD: return "held";
1226 case A_CALL_ALERTING: return "ringing";
1227 case A_CALL_WAITING: return "waiting";
1228 case A_CALL_INCOMING: return "incoming";
1229 default: return "unknown";
1230 }
1231}
1232
1233static int
1234do_gsm_list( ControlClient client, char* args )
1235{
1236 /* check that we have a phone number made of digits */
1237 int count = amodem_get_call_count( android_modem );
1238 int nn;
1239 for (nn = 0; nn < count; nn++) {
1240 ACall call = amodem_get_call( android_modem, nn );
1241 const char* dir;
1242
1243 if (call == NULL)
1244 continue;
1245
1246 if (call->dir == A_CALL_OUTBOUND)
1247 dir = "outbound to ";
1248 else
1249 dir = "inbound from";
1250
1251 control_write( client, "%s %-10s : %s\r\n", dir,
1252 call->number, call_state_to_string(call->state) );
1253 }
1254 return 0;
1255}
1256
1257static int
1258do_gsm_busy( ControlClient client, char* args )
1259{
1260 ACall call;
1261
1262 if (!args) {
1263 control_write( client, "KO: missing argument, try 'gsm busy <phonenumber>'\r\n" );
1264 return -1;
1265 }
1266 call = amodem_find_call_by_number( android_modem, args );
1267 if (call == NULL || call->dir != A_CALL_OUTBOUND) {
1268 control_write( client, "KO: no current outbound call to number '%s' (call %p)\r\n", args, call );
1269 return -1;
1270 }
1271 if ( amodem_disconnect_call( android_modem, args ) < 0 ) {
1272 control_write( client, "KO: could not cancel this number\r\n" );
1273 return -1;
1274 }
1275 return 0;
1276}
1277
1278static int
1279do_gsm_hold( ControlClient client, char* args )
1280{
1281 ACall call;
1282
1283 if (!args) {
1284 control_write( client, "KO: missing argument, try 'gsm out hold <phonenumber>'\r\n" );
1285 return -1;
1286 }
1287 call = amodem_find_call_by_number( android_modem, args );
1288 if (call == NULL) {
1289 control_write( client, "KO: no current call to/from number '%s'\r\n", args );
1290 return -1;
1291 }
1292 if ( amodem_update_call( android_modem, args, A_CALL_HELD ) < 0 ) {
1293 control_write( client, "KO: could put this call on hold\r\n" );
1294 return -1;
1295 }
1296 return 0;
1297}
1298
1299
1300static int
1301do_gsm_accept( ControlClient client, char* args )
1302{
1303 ACall call;
1304
1305 if (!args) {
1306 control_write( client, "KO: missing argument, try 'gsm accept <phonenumber>'\r\n" );
1307 return -1;
1308 }
1309 call = amodem_find_call_by_number( android_modem, args );
1310 if (call == NULL) {
1311 control_write( client, "KO: no current call to/from number '%s'\r\n", args );
1312 return -1;
1313 }
1314 if ( amodem_update_call( android_modem, args, A_CALL_ACTIVE ) < 0 ) {
1315 control_write( client, "KO: could not activate this call\r\n" );
1316 return -1;
1317 }
1318 return 0;
1319}
1320
1321
1322#if 0
1323static const CommandDefRec gsm_in_commands[] =
1324{
1325 { "new", "create a new 'waiting' inbound call",
1326 "'gsm in create <phonenumber>' creates a new inbound phone call, placed in\r\n"
1327 "the 'waiting' state by default, until the system answers/holds/closes it\r\n", NULL
1328 do_gsm_in_create, NULL },
1329
1330 { "hold", "change the state of an oubtound call to 'held'",
1331 "change the state of an outbound call to 'held'. this is only possible\r\n"
1332 "if the call in the 'waiting' or 'active' state\r\n", NULL,
1333 do_gsm_out_hold, NULL },
1334
1335 { "accept", "change the state of an outbound call to 'active'",
1336 "change the state of an outbound call to 'active'. this is only possible\r\n"
1337 "if the call is in the 'waiting' or 'held' state\r\n", NULL,
1338 do_gsm_out_accept, NULL },
1339
1340 { NULL, NULL, NULL, NULL, NULL, NULL }
1341};
1342#endif
1343
1344
1345static const CommandDefRec gsm_commands[] =
1346{
1347 { "list", "list current phone calls",
1348 "'gsm list' lists all inbound and outbound calls and their state\r\n", NULL,
1349 do_gsm_list, NULL },
1350
1351 { "call", "create inbound phone call",
1352 "'gsm call <phonenumber>' allows you to simulate a new inbound call\r\n", NULL,
1353 do_gsm_call, NULL },
1354
1355 { "busy", "close waiting outbound call as busy",
1356 "'gsm busy <remoteNumber>' closes an outbound call, reporting\r\n"
1357 "the remote phone as busy. only possible if the call is 'waiting'.\r\n", NULL,
1358 do_gsm_busy, NULL },
1359
1360 { "hold", "change the state of an oubtound call to 'held'",
1361 "'gsm hold <remoteNumber>' change the state of a call to 'held'. this is only possible\r\n"
1362 "if the call in the 'waiting' or 'active' state\r\n", NULL,
1363 do_gsm_hold, NULL },
1364
1365 { "accept", "change the state of an outbound call to 'active'",
1366 "'gsm accept <remoteNumber>' change the state of a call to 'active'. this is only possible\r\n"
1367 "if the call is in the 'waiting' or 'held' state\r\n", NULL,
1368 do_gsm_accept, NULL },
1369
1370 { "cancel", "disconnect an inbound or outbound phone call",
1371 "'gsm cancel <phonenumber>' allows you to simulate the end of an inbound or outbound call\r\n", NULL,
1372 do_gsm_cancel, NULL },
1373
1374 { "data", "modify data connection state", NULL, help_gsm_data,
1375 do_gsm_data, NULL },
1376
1377 { "voice", "modify voice connection state", NULL, help_gsm_voice,
1378 do_gsm_voice, NULL },
1379
1380 { "status", "display GSM status",
1381 "'gsm status' displays the current state of the GSM emulation\r\n", NULL,
1382 do_gsm_status, NULL },
1383
1384 { NULL, NULL, NULL, NULL, NULL, NULL }
1385};
1386
1387/********************************************************************************************/
1388/********************************************************************************************/
1389/***** ******/
1390/***** S M S C O M M A N D ******/
1391/***** ******/
1392/********************************************************************************************/
1393/********************************************************************************************/
1394
1395static int
1396do_sms_send( ControlClient client, char* args )
1397{
1398 char* p;
1399 int textlen;
1400 SmsAddressRec sender;
1401 SmsPDU* pdus;
1402 int nn;
1403
1404 /* check that we have a phone number made of digits */
1405 if (!args) {
1406 MissingArgument:
1407 control_write( client, "KO: missing argument, try 'sms send <phonenumber> <text message>'\r\n" );
1408 return -1;
1409 }
1410 p = strchr( args, ' ' );
1411 if (!p) {
1412 goto MissingArgument;
1413 }
1414
1415 if ( sms_address_from_str( &sender, args, p - args ) < 0 ) {
1416 control_write( client, "KO: bad phone number format, must be [+](0-9)*\r\n" );
1417 return -1;
1418 }
1419
1420
1421 /* un-secape message text into proper utf-8 (conversion happens in-site) */
1422 p += 1;
1423 textlen = strlen(p);
1424 textlen = sms_utf8_from_message_str( p, textlen, (unsigned char*)p, textlen );
1425 if (textlen < 0) {
1426 control_write( client, "message must be utf8 and can use the following escapes:\r\n"
1427 " \\n for a newline\r\n"
1428 " \\xNN where NN are two hexadecimal numbers\r\n"
1429 " \\uNNNN where NNNN are four hexadecimal numbers\r\n"
1430 " \\\\ to send a '\\' character\r\n\r\n"
1431 " anything else is an error\r\n"
1432 "KO: badly formatted text\r\n" );
1433 return -1;
1434 }
1435
1436 if (!android_modem) {
1437 control_write( client, "KO: modem emulation not running\r\n" );
1438 return -1;
1439 }
1440
1441 /* create a list of SMS PDUs, then send them */
1442 pdus = smspdu_create_deliver_utf8( (cbytes_t)p, textlen, &sender, NULL );
1443 if (pdus == NULL) {
1444 control_write( client, "KO: internal error when creating SMS-DELIVER PDUs\n" );
1445 return -1;
1446 }
1447
1448 for (nn = 0; pdus[nn] != NULL; nn++)
1449 amodem_receive_sms( android_modem, pdus[nn] );
1450
1451 smspdu_free_list( pdus );
1452 return 0;
1453}
1454
1455static int
1456do_sms_sendpdu( ControlClient client, char* args )
1457{
1458 SmsPDU pdu;
1459
1460 /* check that we have a phone number made of digits */
1461 if (!args) {
1462 control_write( client, "KO: missing argument, try 'sms sendpdu <hexstring>'\r\n" );
1463 return -1;
1464 }
1465
1466 if (!android_modem) {
1467 control_write( client, "KO: modem emulation not running\r\n" );
1468 return -1;
1469 }
1470
1471 pdu = smspdu_create_from_hex( args, strlen(args) );
1472 if (pdu == NULL) {
1473 control_write( client, "KO: badly formatted <hexstring>\r\n" );
1474 return -1;
1475 }
1476
1477 amodem_receive_sms( android_modem, pdu );
1478 smspdu_free( pdu );
1479 return 0;
1480}
1481
1482static const CommandDefRec sms_commands[] =
1483{
1484 { "send", "send inbound SMS text message",
1485 "'sms send <phonenumber> <message>' allows you to simulate a new inbound sms message\r\n", NULL,
1486 do_sms_send, NULL },
1487
1488 { "pdu", "send inbound SMS PDU",
1489 "'sms pdu <hexstring>' allows you to simulate a new inbound sms PDU\r\n"
1490 "(used internally when one emulator sends SMS messages to another instance).\r\n"
1491 "you probably don't want to play with this at all\r\n", NULL,
1492 do_sms_sendpdu, NULL },
1493
1494 { NULL, NULL, NULL, NULL, NULL, NULL }
1495};
1496
1497static void
1498do_control_write(void* data, const char* string)
1499{
1500 control_write((ControlClient)data, string);
1501}
1502
1503static int
1504do_power_display( ControlClient client, char* args )
1505{
1506 goldfish_battery_display(do_control_write, client);
1507 return 0;
1508}
1509
1510static int
1511do_ac_state( ControlClient client, char* args )
1512{
1513 if (args) {
1514 if (strcasecmp(args, "on") == 0) {
1515 goldfish_battery_set_prop(1, POWER_SUPPLY_PROP_ONLINE, 1);
1516 return 0;
1517 }
1518 if (strcasecmp(args, "off") == 0) {
1519 goldfish_battery_set_prop(1, POWER_SUPPLY_PROP_ONLINE, 0);
1520 return 0;
1521 }
1522 }
1523
1524 control_write( client, "KO: Usage: \"ac on\" or \"ac off\"\n" );
1525 return -1;
1526}
1527
1528static int
1529do_battery_status( ControlClient client, char* args )
1530{
1531 if (args) {
1532 if (strcasecmp(args, "unknown") == 0) {
1533 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_UNKNOWN);
1534 return 0;
1535 }
1536 if (strcasecmp(args, "charging") == 0) {
1537 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_CHARGING);
1538 return 0;
1539 }
1540 if (strcasecmp(args, "discharging") == 0) {
1541 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_DISCHARGING);
1542 return 0;
1543 }
1544 if (strcasecmp(args, "not-charging") == 0) {
1545 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_NOT_CHARGING);
1546 return 0;
1547 }
1548 if (strcasecmp(args, "full") == 0) {
1549 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_FULL);
1550 return 0;
1551 }
1552 }
1553
1554 control_write( client, "KO: Usage: \"status unknown|charging|discharging|not-charging|full\"\n" );
1555 return -1;
1556}
1557
1558static int
1559do_battery_present( ControlClient client, char* args )
1560{
1561 if (args) {
1562 if (strcasecmp(args, "true") == 0) {
1563 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_PRESENT, 1);
1564 return 0;
1565 }
1566 if (strcasecmp(args, "false") == 0) {
1567 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_PRESENT, 0);
1568 return 0;
1569 }
1570 }
1571
1572 control_write( client, "KO: Usage: \"present true\" or \"present false\"\n" );
1573 return -1;
1574}
1575
1576static int
1577do_battery_health( ControlClient client, char* args )
1578{
1579 if (args) {
1580 if (strcasecmp(args, "unknown") == 0) {
1581 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_UNKNOWN);
1582 return 0;
1583 }
1584 if (strcasecmp(args, "good") == 0) {
1585 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_GOOD);
1586 return 0;
1587 }
1588 if (strcasecmp(args, "overheat") == 0) {
1589 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_OVERHEAT);
1590 return 0;
1591 }
1592 if (strcasecmp(args, "dead") == 0) {
1593 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_DEAD);
1594 return 0;
1595 }
1596 if (strcasecmp(args, "overvoltage") == 0) {
1597 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_OVERVOLTAGE);
1598 return 0;
1599 }
1600 if (strcasecmp(args, "failure") == 0) {
1601 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_UNSPEC_FAILURE);
1602 return 0;
1603 }
1604 }
1605
1606 control_write( client, "KO: Usage: \"health unknown|good|overheat|dead|overvoltage|failure\"\n" );
1607 return -1;
1608}
1609
1610static int
1611do_battery_capacity( ControlClient client, char* args )
1612{
1613 if (args) {
1614 int capacity;
1615
1616 if (sscanf(args, "%d", &capacity) == 1 && capacity >= 0 && capacity <= 100) {
1617 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_CAPACITY, capacity);
1618 return 0;
1619 }
1620 }
1621
1622 control_write( client, "KO: Usage: \"capacity <percentage>\"\n" );
1623 return -1;
1624}
1625
1626
1627static const CommandDefRec power_commands[] =
1628{
1629 { "display", "display battery and charger state",
1630 "display battery and charger state\r\n", NULL,
1631 do_power_display, NULL },
1632
1633 { "ac", "set AC charging state",
1634 "'ac on|off' allows you to set the AC charging state to on or off\r\n", NULL,
1635 do_ac_state, NULL },
1636
1637 { "status", "set battery status",
1638 "'status unknown|charging|discharging|not-charging|full' allows you to set battery status\r\n", NULL,
1639 do_battery_status, NULL },
1640
1641 { "present", "set battery present state",
1642 "'present true|false' allows you to set battery present state to true or false\r\n", NULL,
1643 do_battery_present, NULL },
1644
1645 { "health", "set battery health state",
1646 "'health unknown|good|overheat|dead|overvoltage|failure' allows you to set battery health state\r\n", NULL,
1647 do_battery_health, NULL },
1648
1649 { "capacity", "set battery capacity state",
1650 "'capacity <percentage>' allows you to set battery capacity to a value 0 - 100\r\n", NULL,
1651 do_battery_capacity, NULL },
1652
1653 { NULL, NULL, NULL, NULL, NULL, NULL }
1654};
1655
1656/********************************************************************************************/
1657/********************************************************************************************/
1658/***** ******/
1659/***** E V E N T C O M M A N D S ******/
1660/***** ******/
1661/********************************************************************************************/
1662/********************************************************************************************/
1663
1664
1665static int
1666do_event_send( ControlClient client, char* args )
1667{
1668 char* p;
1669
1670 if (!args) {
1671 control_write( client, "KO: Usage: event send <type>:<code>:<value> ...\r\n" );
1672 return -1;
1673 }
1674
1675 p = args;
1676 while (*p) {
1677 char* q;
1678 int type, code, value, ret;
1679
1680 p += strspn( args, " \t" ); /* skip spaces */
1681 if (*p == 0)
1682 break;
1683
1684 q = p + strcspn( p, " \t" );
1685
1686 if (q == p)
1687 break;
1688
1689 ret = android_event_from_str( p, &type, &code, &value );
1690 if (ret < 0) {
1691 if (ret == -1) {
1692 control_write( client,
1693 "KO: invalid event type in '%.*s', try 'event list types' for valid values\r\n",
1694 q-p, p );
1695 } else if (ret == -2) {
1696 control_write( client,
1697 "KO: invalid event code in '%.*s', try 'event list codes <type>' for valid values\r\n",
1698 q-p, p );
1699 } else {
1700 control_write( client,
1701 "KO: invalid event value in '%.*s', must be an integer\r\n",
1702 q-p, p);
1703 }
1704 return -1;
1705 }
1706
David 'Digit' Turner34f29742010-05-25 18:16:10 -07001707 user_event_generic( type, code, value );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001708 p = q;
1709 }
1710 return 0;
1711}
1712
1713static int
1714do_event_types( ControlClient client, char* args )
1715{
1716 int count = android_event_get_type_count();
1717 int nn;
1718
1719 control_write( client, "event <type> can be an integer or one of the following aliases\r\n" );
1720 for (nn = 0; nn < count; nn++) {
1721 char tmp[16];
1722 char* p = tmp;
1723 char* end = p + sizeof(tmp);
1724 int count2 = android_event_get_code_count( nn );;
1725
1726 p = android_event_bufprint_type_str( p, end, nn );
1727
1728 control_write( client, " %-8s", tmp );
1729 if (count2 > 0)
1730 control_write( client, " (%d code aliases)", count2 );
1731
1732 control_write( client, "\r\n" );
1733 }
1734 return 0;
1735}
1736
1737static int
1738do_event_codes( ControlClient client, char* args )
1739{
1740 int count;
1741 int nn, type, dummy;
1742
1743 if (!args) {
1744 control_write( client, "KO: argument missing, try 'event codes <type>'\r\n" );
1745 return -1;
1746 }
1747
1748 if ( android_event_from_str( args, &type, &dummy, &dummy ) < 0 ) {
1749 control_write( client, "KO: bad argument, see 'event types' for valid values\r\n" );
1750 return -1;
1751 }
1752
1753 count = android_event_get_code_count( type );
1754 if (count == 0) {
1755 control_write( client, "no code aliases defined for this type\r\n" );
1756 } else {
1757 control_write( client, "type '%s' accepts the following <code> aliases:\r\n",
1758 args );
1759 for (nn = 0; nn < count; nn++) {
1760 char temp[20], *p = temp, *end = p + sizeof(temp);
1761 android_event_bufprint_code_str( p, end, type, nn );
1762 control_write( client, " %-12s\r\n", temp );
1763 }
1764 }
1765
1766 return 0;
1767}
1768
1769static __inline__ int
1770utf8_next( unsigned char* *pp, unsigned char* end )
1771{
1772 unsigned char* p = *pp;
1773 int result = -1;
1774
1775 if (p < end) {
1776 int c= *p++;
1777 if (c >= 128) {
1778 if ((c & 0xe0) == 0xc0)
1779 c &= 0x1f;
1780 else if ((c & 0xf0) == 0xe0)
1781 c &= 0x0f;
1782 else
1783 c &= 0x07;
1784
1785 while (p < end && (p[0] & 0xc0) == 0x80) {
1786 c = (c << 6) | (p[0] & 0x3f);
1787 }
1788 }
1789 result = c;
1790 *pp = p;
1791 }
1792 return result;
1793}
1794
1795static int
1796do_event_text( ControlClient client, char* args )
1797{
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001798 AKeycodeBuffer keycodes;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001799 unsigned char* p = (unsigned char*) args;
1800 unsigned char* end = p + strlen(args);
1801 int textlen;
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001802 const AKeyCharmap* charmap;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001803
1804 if (!args) {
1805 control_write( client, "KO: argument missing, try 'event text <message>'\r\n" );
1806 return -1;
1807 }
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001808
1809 /* Get default charmap. */
Vladimir Chtchetkine43552dc2010-07-22 11:23:19 -07001810 charmap = android_get_default_charmap();
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001811 if (charmap == NULL) {
1812 control_write( client, "KO: no character map active in current device layout/config\r\n" );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001813 return -1;
1814 }
1815
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001816 keycodes.keycode_count = 0;
1817
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001818 /* un-secape message text into proper utf-8 (conversion happens in-site) */
1819 textlen = strlen((char*)p);
1820 textlen = sms_utf8_from_message_str( args, textlen, (unsigned char*)p, textlen );
1821 if (textlen < 0) {
1822 control_write( client, "message must be utf8 and can use the following escapes:\r\n"
1823 " \\n for a newline\r\n"
1824 " \\xNN where NN are two hexadecimal numbers\r\n"
1825 " \\uNNNN where NNNN are four hexadecimal numbers\r\n"
1826 " \\\\ to send a '\\' character\r\n\r\n"
1827 " anything else is an error\r\n"
1828 "KO: badly formatted text\r\n" );
1829 return -1;
1830 }
1831
1832 end = p + textlen;
1833 while (p < end) {
1834 int c = utf8_next( &p, end );
1835 if (c <= 0)
1836 break;
1837
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001838 android_charmap_reverse_map_unicode( NULL, (unsigned)c, 1, &keycodes );
1839 android_charmap_reverse_map_unicode( NULL, (unsigned)c, 0, &keycodes );
1840 android_keycodes_flush( &keycodes );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001841 }
1842
1843 return 0;
1844}
1845
1846static const CommandDefRec event_commands[] =
1847{
1848 { "send", "send a series of events to the kernel",
1849 "'event send <type>:<code>:<value> ...' allows your to send one or more hardware events\r\n"
1850 "to the Android kernel. you can use text names or integers for <type> and <code>\r\n", NULL,
1851 do_event_send, NULL },
1852
1853 { "types", "list all <type> aliases",
1854 "'event types' list all <type> string aliases supported by the 'event' subcommands\r\n",
1855 NULL, do_event_types, NULL },
1856
1857 { "codes", "list all <code> aliases for a given <type>",
1858 "'event codes <type>' lists all <code> string aliases for a given event <type>\r\n",
1859 NULL, do_event_codes, NULL },
1860
1861 { "text", "simulate keystrokes from a given text",
1862 "'event text <message>' allows you to simulate keypresses to generate a given text\r\n"
1863 "message. <message> must be an utf-8 string. Unicode points will be reverse-mapped\r\n"
1864 "according to the current device keyboard. unsupported characters will be discarded\r\n"
1865 "silently\r\n", NULL, do_event_text, NULL },
1866
1867 { NULL, NULL, NULL, NULL, NULL, NULL }
1868};
1869
1870
1871/********************************************************************************************/
1872/********************************************************************************************/
1873/***** ******/
1874/***** V M C O M M A N D S ******/
1875/***** ******/
1876/********************************************************************************************/
1877/********************************************************************************************/
1878
1879static int
1880do_avd_stop( ControlClient client, char* args )
1881{
1882 if (!vm_running) {
1883 control_write( client, "KO: virtual device already stopped\r\n" );
1884 return -1;
1885 }
1886 vm_stop(EXCP_INTERRUPT);
1887 return 0;
1888}
1889
1890static int
1891do_avd_start( ControlClient client, char* args )
1892{
1893 if (vm_running) {
1894 control_write( client, "KO: virtual device already running\r\n" );
1895 return -1;
1896 }
1897 vm_start();
1898 return 0;
1899}
1900
1901static int
1902do_avd_status( ControlClient client, char* args )
1903{
1904 control_write( client, "virtual device is %s\r\n", vm_running ? "running" : "stopped" );
1905 return 0;
1906}
1907
1908static int
1909do_avd_name( ControlClient client, char* args )
1910{
1911 control_write( client, "%s\r\n", avdInfo_getName(android_avdInfo) );
1912 return 0;
1913}
1914
1915static const CommandDefRec vm_commands[] =
1916{
1917 { "stop", "stop the virtual device",
1918 "'avd stop' stops the virtual device immediately, use 'avd start' to continue execution\r\n",
1919 NULL, do_avd_stop, NULL },
1920
1921 { "start", "start/restart the virtual device",
1922 "'avd start' will start or continue the virtual device, use 'avd stop' to stop it\r\n",
1923 NULL, do_avd_start, NULL },
1924
1925 { "status", "query virtual device status",
1926 "'avd status' will indicate wether the virtual device is running or not\r\n",
1927 NULL, do_avd_status, NULL },
1928
1929 { "name", "query virtual device name",
1930 "'avd name' will return the name of this virtual device\r\n",
1931 NULL, do_avd_name, NULL },
1932
1933 { NULL, NULL, NULL, NULL, NULL, NULL }
1934};
1935
1936/********************************************************************************************/
1937/********************************************************************************************/
1938/***** ******/
1939/***** G E O C O M M A N D S ******/
1940/***** ******/
1941/********************************************************************************************/
1942/********************************************************************************************/
1943
1944static int
1945do_geo_nmea( ControlClient client, char* args )
1946{
1947 if (!args) {
1948 control_write( client, "KO: NMEA sentence missing, try 'help geo nmea'\r\n" );
1949 return -1;
1950 }
1951 if (!android_gps_cs) {
1952 control_write( client, "KO: no GPS emulation in this virtual device\r\n" );
1953 return -1;
1954 }
1955 android_gps_send_nmea( args );
1956 return 0;
1957}
1958
1959static int
1960do_geo_fix( ControlClient client, char* args )
1961{
1962#define MAX_GEO_PARAMS 3
1963 char* p = args;
1964 int n_params = 0;
1965 double params[ MAX_GEO_PARAMS ];
1966
1967 static int last_time = 0;
1968 static double last_altitude = 0.;
1969
1970 if (!p)
1971 p = "";
1972
1973 /* tokenize */
1974 while (*p) {
1975 char* end;
1976 double val = strtod( p, &end );
1977
1978 if (end == p) {
1979 control_write( client, "KO: argument '%s' is not a number\n", p );
1980 return -1;
1981 }
1982
1983 params[n_params++] = val;
1984 if (n_params >= MAX_GEO_PARAMS)
1985 break;
1986
1987 p = end;
1988 while (*p && (p[0] == ' ' || p[0] == '\t'))
1989 p += 1;
1990 }
1991
1992 /* sanity check */
1993 if (n_params < 2) {
1994 control_write( client, "KO: not enough arguments: see 'help geo fix' for details\r\n" );
1995 return -1;
1996 }
1997
1998 /* generate an NMEA sentence for this fix */
1999 {
2000 STRALLOC_DEFINE(s);
2001 double val;
2002 int deg, min;
2003 char hemi;
2004
2005 /* first, the time */
2006 stralloc_add_format( s, "$GPGGA,%06d", last_time );
2007 last_time ++;
2008
2009 /* then the latitude */
2010 hemi = 'N';
2011 val = params[1];
2012 if (val < 0) {
2013 hemi = 'S';
2014 val = -val;
2015 }
2016 deg = (int) val;
2017 min = 60*(val - deg);
2018 val = val - min/60.;
2019 stralloc_add_format( s, ",%02d%02d.%04d,%c", deg, min, (int)(val * 10000), hemi );
2020
2021 /* the longitude */
2022 hemi = 'E';
2023 val = params[0];
2024 if (val < 0) {
2025 hemi = 'W';
2026 val = -val;
2027 }
2028 deg = (int) val;
2029 min = 60*(val - deg);
2030 val = val - min/60.;
2031 stralloc_add_format( s, ",%02d%02d.%04d,%c", deg, min, (int)(val * 10000), hemi );
2032
2033 /* bogus fix quality, empty satellite count and dilutions */
2034 stralloc_add_str( s, ",1,,,," );
2035
2036 /* optional altitude */
2037 if (n_params >= 3) {
2038 stralloc_add_format( s, "%.1g", params[2] );
2039 last_altitude = params[2];
2040 } else {
2041 stralloc_add_str( s, "," );
2042 }
2043 /* bogus rest and checksum */
2044 stralloc_add_str( s, ",,,*47" );
2045
2046 /* send it, then free */
2047 android_gps_send_nmea( stralloc_cstr(s) );
2048 stralloc_reset( s );
2049 }
2050 return 0;
2051}
2052
2053static const CommandDefRec geo_commands[] =
2054{
2055 { "nmea", "send an GPS NMEA sentence",
2056 "'geo nema <sentence>' sends a NMEA 0183 sentence to the emulated device, as\r\n"
2057 "if it came from an emulated GPS modem. <sentence> must begin with '$GP'. only\r\n"
2058 "'$GPGGA' and '$GPRCM' sentences are supported at the moment.\r\n",
2059 NULL, do_geo_nmea, NULL },
2060
2061 { "fix", "send a simple GPS fix",
2062 "'geo fix <longitude> <latitude> [<altitude>]' allows you to send a\r\n"
2063 "simple GPS fix to the emulated system. the parameters are:\r\n\r\n"
2064 " <longitude> longitude, in decimal degrees\r\n"
2065 " <latitude> latitude, in decimal degrees\r\n"
2066 " <altitude> optional altitude in meters\r\n"
2067 "\r\n",
2068 NULL, do_geo_fix, NULL },
2069
2070 { NULL, NULL, NULL, NULL, NULL, NULL }
2071};
2072
2073
2074/********************************************************************************************/
2075/********************************************************************************************/
2076/***** ******/
2077/***** M A I N C O M M A N D S ******/
2078/***** ******/
2079/********************************************************************************************/
2080/********************************************************************************************/
2081
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002082static int
2083do_window_scale( ControlClient client, char* args )
2084{
2085 double scale;
2086 int is_dpi = 0;
2087 char* end;
2088
2089 if (!args) {
2090 control_write( client, "KO: argument missing, try 'window scale <scale>'\r\n" );
2091 return -1;
2092 }
2093
2094 scale = strtol( args, &end, 10 );
2095 if (end > args && !memcmp( end, "dpi", 4 )) {
2096 is_dpi = 1;
2097 }
2098 else {
2099 scale = strtod( args, &end );
2100 if (end == args || end[0]) {
2101 control_write( client, "KO: argument <scale> must be a real number, or an integer followed by 'dpi'\r\n" );
2102 return -1;
2103 }
2104 }
2105
Vladimir Chtchetkine2fa51732010-07-16 11:19:48 -07002106 android_ui_set_window_scale( scale, is_dpi );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002107 return 0;
2108}
2109
2110static const CommandDefRec window_commands[] =
2111{
2112 { "scale", "change the window scale",
2113 "'window scale <scale>' allows you to change the scale of the emulator window at runtime\r\n"
2114 "<scale> must be either a real number between 0.1 and 3.0, or an integer followed by\r\n"
2115 "the 'dpi' prefix (as in '120dpi')\r\n",
2116 NULL, do_window_scale, NULL },
2117
2118 { NULL, NULL, NULL, NULL, NULL, NULL }
2119};
2120
2121/********************************************************************************************/
2122/********************************************************************************************/
2123/***** ******/
2124/***** M A I N C O M M A N D S ******/
2125/***** ******/
2126/********************************************************************************************/
2127/********************************************************************************************/
2128
2129static int
2130do_kill( ControlClient client, char* args )
2131{
2132 control_write( client, "OK: killing emulator, bye bye\r\n" );
2133 exit(0);
2134}
2135
2136static const CommandDefRec main_commands[] =
2137{
2138 { "help|h|?", "print a list of commands", NULL, NULL, do_help, NULL },
2139
2140 { "event", "simulate hardware events",
2141 "allows you to send fake hardware events to the kernel\r\n", NULL,
2142 NULL, event_commands },
2143
2144 { "geo", "Geo-location commands",
2145 "allows you to change Geo-related settings, or to send GPS NMEA sentences\r\n", NULL,
2146 NULL, geo_commands },
2147
2148 { "gsm", "GSM related commands",
2149 "allows you to change GSM-related settings, or to make a new inbound phone call\r\n", NULL,
2150 NULL, gsm_commands },
2151
2152 { "kill", "kill the emulator instance", NULL, NULL,
2153 do_kill, NULL },
2154
2155 { "network", "manage network settings",
2156 "allows you to manage the settings related to the network data connection of the\r\n"
2157 "emulated device.\r\n", NULL,
2158 NULL, network_commands },
2159
2160 { "power", "power related commands",
2161 "allows to change battery and AC power status\r\n", NULL,
2162 NULL, power_commands },
2163
2164 { "quit|exit", "quit control session", NULL, NULL,
2165 do_quit, NULL },
2166
2167 { "redir", "manage port redirections",
2168 "allows you to add, list and remove UDP and/or PORT redirection from the host to the device\r\n"
2169 "as an example, 'redir tcp:5000:6000' will route any packet sent to the host's TCP port 5000\r\n"
2170 "to TCP port 6000 of the emulated device\r\n", NULL,
2171 NULL, redir_commands },
2172
2173 { "sms", "SMS related commands",
2174 "allows you to simulate an inbound SMS\r\n", NULL,
2175 NULL, sms_commands },
2176
2177 { "avd", "manager virtual device state",
2178 "allows to change (e.g. start/stop) the virtual device state\r\n", NULL,
2179 NULL, vm_commands },
2180
2181 { "window", "manage emulator window",
2182 "allows you to modify the emulator window\r\n", NULL,
2183 NULL, window_commands },
2184
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002185 { NULL, NULL, NULL, NULL, NULL, NULL }
2186};
2187
2188
2189static ControlGlobalRec _g_global;
2190
2191int
2192control_console_start( int port )
2193{
2194 return control_global_init( &_g_global, port );
2195}