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