blob: c47768e6122aa6c32b5248cc673f486128555a77 [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/***** ******/
Jaime Lopez1a000852010-07-21 18:03:58 -0700975/***** C D M A M O D E M ******/
976/***** ******/
977/********************************************************************************************/
978/********************************************************************************************/
979
980static const struct {
981 const char * name;
982 const char * display;
983 ACdmaSubscriptionSource source;
984} _cdma_subscription_sources[] = {
985 { "nv", "Read subscription from non-volatile RAM", A_SUBSCRIPTION_NVRAM },
986 { "ruim", "Read subscription from RUIM", A_SUBSCRIPTION_RUIM },
987};
988
989static void
990dump_subscription_sources( ControlClient client )
991{
992 int i;
993 for (i = 0;
994 i < sizeof(_cdma_subscription_sources) / sizeof(_cdma_subscription_sources[0]);
995 i++) {
996 control_write( client, " %s: %s\r\n",
997 _cdma_subscription_sources[i].name,
998 _cdma_subscription_sources[i].display );
999 }
1000}
1001
1002static void
1003describe_subscription_source( ControlClient client )
1004{
1005 control_write( client,
1006 "'cdma ssource <ssource>' allows you to specify where to read the subscription from\r\n" );
1007 dump_subscription_sources( client );
1008}
1009
1010static int
1011do_cdma_ssource( ControlClient client, char* args )
1012{
1013 int nn;
1014 if (!args) {
1015 control_write( client, "KO: missing argument, try 'cdma ssource <source>'\r\n" );
1016 return -1;
1017 }
1018
1019 for (nn = 0; ; nn++) {
1020 const char* name = _cdma_subscription_sources[nn].name;
1021 ACdmaSubscriptionSource ssource = _cdma_subscription_sources[nn].source;
1022
1023 if (!name)
1024 break;
1025
1026 if (!strcasecmp( args, name )) {
1027 amodem_set_cdma_subscription_source( android_modem, ssource );
1028 return 0;
1029 }
1030 }
1031 control_write( client, "KO: Don't know source %s\r\n", args );
1032 return -1;
1033}
1034
1035static int
1036do_cdma_prl_version( ControlClient client, char * args )
1037{
1038 int version = 0;
1039 char *endptr;
1040
1041 if (!args) {
1042 control_write( client, "KO: missing argument, try 'cdma prl_version <version>'\r\n");
1043 return -1;
1044 }
1045
1046 version = strtol(args, &endptr, 0);
1047 if (endptr != args) {
1048 amodem_set_cdma_prl_version( android_modem, version );
1049 }
1050}
1051/********************************************************************************************/
1052/********************************************************************************************/
1053/***** ******/
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001054/***** G S M M O D E M ******/
1055/***** ******/
1056/********************************************************************************************/
1057/********************************************************************************************/
1058
1059static const struct {
1060 const char* name;
1061 const char* display;
1062 ARegistrationState state;
1063} _gsm_states[] = {
1064 { "unregistered", "no network available", A_REGISTRATION_UNREGISTERED },
1065 { "home", "on local network, non-roaming", A_REGISTRATION_HOME },
1066 { "roaming", "on roaming network", A_REGISTRATION_ROAMING },
1067 { "searching", "searching networks", A_REGISTRATION_SEARCHING },
1068 { "denied", "emergency calls only", A_REGISTRATION_DENIED },
1069 { "off", "same as 'unregistered'", A_REGISTRATION_UNREGISTERED },
1070 { "on", "same as 'home'", A_REGISTRATION_HOME },
1071 { NULL, NULL, A_REGISTRATION_UNREGISTERED }
1072};
1073
1074static const char*
1075gsm_state_to_string( ARegistrationState state )
1076{
1077 int nn;
1078 for (nn = 0; _gsm_states[nn].name != NULL; nn++) {
1079 if (state == _gsm_states[nn].state)
1080 return _gsm_states[nn].name;
1081 }
1082 return "<unknown>";
1083}
1084
1085static int
1086do_gsm_status( ControlClient client, char* args )
1087{
1088 if (args) {
1089 control_write( client, "KO: no argument required\r\n" );
1090 return -1;
1091 }
1092 if (!android_modem) {
1093 control_write( client, "KO: modem emulation not running\r\n" );
1094 return -1;
1095 }
1096 control_write( client, "gsm voice state: %s\r\n",
1097 gsm_state_to_string(
1098 amodem_get_voice_registration(android_modem) ) );
1099 control_write( client, "gsm data state: %s\r\n",
1100 gsm_state_to_string(
1101 amodem_get_data_registration(android_modem) ) );
1102 return 0;
1103}
1104
1105
1106static void
1107help_gsm_data( ControlClient client )
1108{
1109 int nn;
1110 control_write( client,
1111 "the 'gsm data <state>' allows you to change the state of your GPRS connection\r\n"
1112 "valid values for <state> are the following:\r\n\r\n" );
1113 for (nn = 0; ; nn++) {
1114 const char* name = _gsm_states[nn].name;
1115 const char* display = _gsm_states[nn].display;
1116
1117 if (!name)
1118 break;
1119
1120 control_write( client, " %-15s %s\r\n", name, display );
1121 }
1122 control_write( client, "\r\n" );
1123}
1124
1125
1126static int
1127do_gsm_data( ControlClient client, char* args )
1128{
1129 int nn;
1130
1131 if (!args) {
1132 control_write( client, "KO: missing argument, try 'gsm data <state>'\r\n" );
1133 return -1;
1134 }
1135
1136 for (nn = 0; ; nn++) {
1137 const char* name = _gsm_states[nn].name;
1138 ARegistrationState state = _gsm_states[nn].state;
1139
1140 if (!name)
1141 break;
1142
1143 if ( !strcmp( args, name ) ) {
1144 if (!android_modem) {
1145 control_write( client, "KO: modem emulation not running\r\n" );
1146 return -1;
1147 }
1148 amodem_set_data_registration( android_modem, state );
1149 qemu_net_disable = (state != A_REGISTRATION_HOME &&
1150 state != A_REGISTRATION_ROAMING );
1151 return 0;
1152 }
1153 }
1154 control_write( client, "KO: bad GSM data state name, try 'help gsm data' for list of valid values\r\n" );
1155 return -1;
1156}
1157
1158static void
1159help_gsm_voice( ControlClient client )
1160{
1161 int nn;
1162 control_write( client,
1163 "the 'gsm voice <state>' allows you to change the state of your GPRS connection\r\n"
1164 "valid values for <state> are the following:\r\n\r\n" );
1165 for (nn = 0; ; nn++) {
1166 const char* name = _gsm_states[nn].name;
1167 const char* display = _gsm_states[nn].display;
1168
1169 if (!name)
1170 break;
1171
1172 control_write( client, " %-15s %s\r\n", name, display );
1173 }
1174 control_write( client, "\r\n" );
1175}
1176
1177
1178static int
1179do_gsm_voice( ControlClient client, char* args )
1180{
1181 int nn;
1182
1183 if (!args) {
1184 control_write( client, "KO: missing argument, try 'gsm voice <state>'\r\n" );
1185 return -1;
1186 }
1187
1188 for (nn = 0; ; nn++) {
1189 const char* name = _gsm_states[nn].name;
1190 ARegistrationState state = _gsm_states[nn].state;
1191
1192 if (!name)
1193 break;
1194
1195 if ( !strcmp( args, name ) ) {
1196 if (!android_modem) {
1197 control_write( client, "KO: modem emulation not running\r\n" );
1198 return -1;
1199 }
1200 amodem_set_voice_registration( android_modem, state );
1201 return 0;
1202 }
1203 }
1204 control_write( client, "KO: bad GSM data state name, try 'help gsm voice' for list of valid values\r\n" );
1205 return -1;
1206}
1207
1208
1209static int
1210gsm_check_number( char* args )
1211{
1212 int nn;
1213
1214 for (nn = 0; args[nn] != 0; nn++) {
1215 int c = args[nn];
1216 if ( !isdigit(c) && c != '+' && c != '#' ) {
1217 return -1;
1218 }
1219 }
1220 if (nn == 0)
1221 return -1;
1222
1223 return 0;
1224}
1225
1226static int
1227do_gsm_call( ControlClient client, char* args )
1228{
1229 /* check that we have a phone number made of digits */
1230 if (!args) {
1231 control_write( client, "KO: missing argument, try 'gsm call <phonenumber>'\r\n" );
1232 return -1;
1233 }
1234
1235 if (gsm_check_number(args)) {
1236 control_write( client, "KO: bad phone number format, use digits, # and + only\r\n" );
1237 return -1;
1238 }
1239
1240 if (!android_modem) {
1241 control_write( client, "KO: modem emulation not running\r\n" );
1242 return -1;
1243 }
1244 amodem_add_inbound_call( android_modem, args );
1245 return 0;
1246}
1247
1248static int
1249do_gsm_cancel( ControlClient client, char* args )
1250{
1251 if (!args) {
1252 control_write( client, "KO: missing argument, try 'gsm call <phonenumber>'\r\n" );
1253 return -1;
1254 }
1255 if (gsm_check_number(args)) {
1256 control_write( client, "KO: bad phone number format, use digits, # and + only\r\n" );
1257 return -1;
1258 }
1259 if (!android_modem) {
1260 control_write( client, "KO: modem emulation not running\r\n" );
1261 return -1;
1262 }
1263 if ( amodem_disconnect_call( android_modem, args ) < 0 ) {
1264 control_write( client, "KO: could not cancel this number\r\n" );
1265 return -1;
1266 }
1267 return 0;
1268}
1269
1270
1271static const char*
1272call_state_to_string( ACallState state )
1273{
1274 switch (state) {
1275 case A_CALL_ACTIVE: return "active";
1276 case A_CALL_HELD: return "held";
1277 case A_CALL_ALERTING: return "ringing";
1278 case A_CALL_WAITING: return "waiting";
1279 case A_CALL_INCOMING: return "incoming";
1280 default: return "unknown";
1281 }
1282}
1283
1284static int
1285do_gsm_list( ControlClient client, char* args )
1286{
1287 /* check that we have a phone number made of digits */
1288 int count = amodem_get_call_count( android_modem );
1289 int nn;
1290 for (nn = 0; nn < count; nn++) {
1291 ACall call = amodem_get_call( android_modem, nn );
1292 const char* dir;
1293
1294 if (call == NULL)
1295 continue;
1296
1297 if (call->dir == A_CALL_OUTBOUND)
1298 dir = "outbound to ";
1299 else
1300 dir = "inbound from";
1301
1302 control_write( client, "%s %-10s : %s\r\n", dir,
1303 call->number, call_state_to_string(call->state) );
1304 }
1305 return 0;
1306}
1307
1308static int
1309do_gsm_busy( ControlClient client, char* args )
1310{
1311 ACall call;
1312
1313 if (!args) {
1314 control_write( client, "KO: missing argument, try 'gsm busy <phonenumber>'\r\n" );
1315 return -1;
1316 }
1317 call = amodem_find_call_by_number( android_modem, args );
1318 if (call == NULL || call->dir != A_CALL_OUTBOUND) {
1319 control_write( client, "KO: no current outbound call to number '%s' (call %p)\r\n", args, call );
1320 return -1;
1321 }
1322 if ( amodem_disconnect_call( android_modem, args ) < 0 ) {
1323 control_write( client, "KO: could not cancel this number\r\n" );
1324 return -1;
1325 }
1326 return 0;
1327}
1328
1329static int
1330do_gsm_hold( ControlClient client, char* args )
1331{
1332 ACall call;
1333
1334 if (!args) {
1335 control_write( client, "KO: missing argument, try 'gsm out hold <phonenumber>'\r\n" );
1336 return -1;
1337 }
1338 call = amodem_find_call_by_number( android_modem, args );
1339 if (call == NULL) {
1340 control_write( client, "KO: no current call to/from number '%s'\r\n", args );
1341 return -1;
1342 }
1343 if ( amodem_update_call( android_modem, args, A_CALL_HELD ) < 0 ) {
1344 control_write( client, "KO: could put this call on hold\r\n" );
1345 return -1;
1346 }
1347 return 0;
1348}
1349
1350
1351static int
1352do_gsm_accept( ControlClient client, char* args )
1353{
1354 ACall call;
1355
1356 if (!args) {
1357 control_write( client, "KO: missing argument, try 'gsm accept <phonenumber>'\r\n" );
1358 return -1;
1359 }
1360 call = amodem_find_call_by_number( android_modem, args );
1361 if (call == NULL) {
1362 control_write( client, "KO: no current call to/from number '%s'\r\n", args );
1363 return -1;
1364 }
1365 if ( amodem_update_call( android_modem, args, A_CALL_ACTIVE ) < 0 ) {
1366 control_write( client, "KO: could not activate this call\r\n" );
1367 return -1;
1368 }
1369 return 0;
1370}
1371
1372
1373#if 0
1374static const CommandDefRec gsm_in_commands[] =
1375{
1376 { "new", "create a new 'waiting' inbound call",
1377 "'gsm in create <phonenumber>' creates a new inbound phone call, placed in\r\n"
1378 "the 'waiting' state by default, until the system answers/holds/closes it\r\n", NULL
1379 do_gsm_in_create, NULL },
1380
1381 { "hold", "change the state of an oubtound call to 'held'",
1382 "change the state of an outbound call to 'held'. this is only possible\r\n"
1383 "if the call in the 'waiting' or 'active' state\r\n", NULL,
1384 do_gsm_out_hold, NULL },
1385
1386 { "accept", "change the state of an outbound call to 'active'",
1387 "change the state of an outbound call to 'active'. this is only possible\r\n"
1388 "if the call is in the 'waiting' or 'held' state\r\n", NULL,
1389 do_gsm_out_accept, NULL },
1390
1391 { NULL, NULL, NULL, NULL, NULL, NULL }
1392};
1393#endif
1394
1395
Jaime Lopez1a000852010-07-21 18:03:58 -07001396static const CommandDefRec cdma_commands[] =
1397{
1398 { "ssource", "Set the current CDMA subscription source",
1399 NULL, describe_subscription_source,
1400 do_cdma_ssource, NULL },
1401};
1402
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001403static const CommandDefRec gsm_commands[] =
1404{
1405 { "list", "list current phone calls",
1406 "'gsm list' lists all inbound and outbound calls and their state\r\n", NULL,
1407 do_gsm_list, NULL },
1408
1409 { "call", "create inbound phone call",
1410 "'gsm call <phonenumber>' allows you to simulate a new inbound call\r\n", NULL,
1411 do_gsm_call, NULL },
1412
1413 { "busy", "close waiting outbound call as busy",
1414 "'gsm busy <remoteNumber>' closes an outbound call, reporting\r\n"
1415 "the remote phone as busy. only possible if the call is 'waiting'.\r\n", NULL,
1416 do_gsm_busy, NULL },
1417
1418 { "hold", "change the state of an oubtound call to 'held'",
1419 "'gsm hold <remoteNumber>' change the state of a call to 'held'. this is only possible\r\n"
1420 "if the call in the 'waiting' or 'active' state\r\n", NULL,
1421 do_gsm_hold, NULL },
1422
1423 { "accept", "change the state of an outbound call to 'active'",
1424 "'gsm accept <remoteNumber>' change the state of a call to 'active'. this is only possible\r\n"
1425 "if the call is in the 'waiting' or 'held' state\r\n", NULL,
1426 do_gsm_accept, NULL },
1427
1428 { "cancel", "disconnect an inbound or outbound phone call",
1429 "'gsm cancel <phonenumber>' allows you to simulate the end of an inbound or outbound call\r\n", NULL,
1430 do_gsm_cancel, NULL },
1431
1432 { "data", "modify data connection state", NULL, help_gsm_data,
1433 do_gsm_data, NULL },
1434
1435 { "voice", "modify voice connection state", NULL, help_gsm_voice,
1436 do_gsm_voice, NULL },
1437
1438 { "status", "display GSM status",
1439 "'gsm status' displays the current state of the GSM emulation\r\n", NULL,
1440 do_gsm_status, NULL },
1441
1442 { NULL, NULL, NULL, NULL, NULL, NULL }
1443};
1444
1445/********************************************************************************************/
1446/********************************************************************************************/
1447/***** ******/
1448/***** S M S C O M M A N D ******/
1449/***** ******/
1450/********************************************************************************************/
1451/********************************************************************************************/
1452
1453static int
1454do_sms_send( ControlClient client, char* args )
1455{
1456 char* p;
1457 int textlen;
1458 SmsAddressRec sender;
1459 SmsPDU* pdus;
1460 int nn;
1461
1462 /* check that we have a phone number made of digits */
1463 if (!args) {
1464 MissingArgument:
1465 control_write( client, "KO: missing argument, try 'sms send <phonenumber> <text message>'\r\n" );
1466 return -1;
1467 }
1468 p = strchr( args, ' ' );
1469 if (!p) {
1470 goto MissingArgument;
1471 }
1472
1473 if ( sms_address_from_str( &sender, args, p - args ) < 0 ) {
1474 control_write( client, "KO: bad phone number format, must be [+](0-9)*\r\n" );
1475 return -1;
1476 }
1477
1478
1479 /* un-secape message text into proper utf-8 (conversion happens in-site) */
1480 p += 1;
1481 textlen = strlen(p);
1482 textlen = sms_utf8_from_message_str( p, textlen, (unsigned char*)p, textlen );
1483 if (textlen < 0) {
1484 control_write( client, "message must be utf8 and can use the following escapes:\r\n"
1485 " \\n for a newline\r\n"
1486 " \\xNN where NN are two hexadecimal numbers\r\n"
1487 " \\uNNNN where NNNN are four hexadecimal numbers\r\n"
1488 " \\\\ to send a '\\' character\r\n\r\n"
1489 " anything else is an error\r\n"
1490 "KO: badly formatted text\r\n" );
1491 return -1;
1492 }
1493
1494 if (!android_modem) {
1495 control_write( client, "KO: modem emulation not running\r\n" );
1496 return -1;
1497 }
1498
1499 /* create a list of SMS PDUs, then send them */
1500 pdus = smspdu_create_deliver_utf8( (cbytes_t)p, textlen, &sender, NULL );
1501 if (pdus == NULL) {
1502 control_write( client, "KO: internal error when creating SMS-DELIVER PDUs\n" );
1503 return -1;
1504 }
1505
1506 for (nn = 0; pdus[nn] != NULL; nn++)
1507 amodem_receive_sms( android_modem, pdus[nn] );
1508
1509 smspdu_free_list( pdus );
1510 return 0;
1511}
1512
1513static int
1514do_sms_sendpdu( ControlClient client, char* args )
1515{
1516 SmsPDU pdu;
1517
1518 /* check that we have a phone number made of digits */
1519 if (!args) {
1520 control_write( client, "KO: missing argument, try 'sms sendpdu <hexstring>'\r\n" );
1521 return -1;
1522 }
1523
1524 if (!android_modem) {
1525 control_write( client, "KO: modem emulation not running\r\n" );
1526 return -1;
1527 }
1528
1529 pdu = smspdu_create_from_hex( args, strlen(args) );
1530 if (pdu == NULL) {
1531 control_write( client, "KO: badly formatted <hexstring>\r\n" );
1532 return -1;
1533 }
1534
1535 amodem_receive_sms( android_modem, pdu );
1536 smspdu_free( pdu );
1537 return 0;
1538}
1539
1540static const CommandDefRec sms_commands[] =
1541{
1542 { "send", "send inbound SMS text message",
1543 "'sms send <phonenumber> <message>' allows you to simulate a new inbound sms message\r\n", NULL,
1544 do_sms_send, NULL },
1545
1546 { "pdu", "send inbound SMS PDU",
1547 "'sms pdu <hexstring>' allows you to simulate a new inbound sms PDU\r\n"
1548 "(used internally when one emulator sends SMS messages to another instance).\r\n"
1549 "you probably don't want to play with this at all\r\n", NULL,
1550 do_sms_sendpdu, NULL },
1551
1552 { NULL, NULL, NULL, NULL, NULL, NULL }
1553};
1554
1555static void
1556do_control_write(void* data, const char* string)
1557{
1558 control_write((ControlClient)data, string);
1559}
1560
1561static int
1562do_power_display( ControlClient client, char* args )
1563{
1564 goldfish_battery_display(do_control_write, client);
1565 return 0;
1566}
1567
1568static int
1569do_ac_state( ControlClient client, char* args )
1570{
1571 if (args) {
1572 if (strcasecmp(args, "on") == 0) {
1573 goldfish_battery_set_prop(1, POWER_SUPPLY_PROP_ONLINE, 1);
1574 return 0;
1575 }
1576 if (strcasecmp(args, "off") == 0) {
1577 goldfish_battery_set_prop(1, POWER_SUPPLY_PROP_ONLINE, 0);
1578 return 0;
1579 }
1580 }
1581
1582 control_write( client, "KO: Usage: \"ac on\" or \"ac off\"\n" );
1583 return -1;
1584}
1585
1586static int
1587do_battery_status( ControlClient client, char* args )
1588{
1589 if (args) {
1590 if (strcasecmp(args, "unknown") == 0) {
1591 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_UNKNOWN);
1592 return 0;
1593 }
1594 if (strcasecmp(args, "charging") == 0) {
1595 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_CHARGING);
1596 return 0;
1597 }
1598 if (strcasecmp(args, "discharging") == 0) {
1599 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_DISCHARGING);
1600 return 0;
1601 }
1602 if (strcasecmp(args, "not-charging") == 0) {
1603 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_NOT_CHARGING);
1604 return 0;
1605 }
1606 if (strcasecmp(args, "full") == 0) {
1607 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_FULL);
1608 return 0;
1609 }
1610 }
1611
1612 control_write( client, "KO: Usage: \"status unknown|charging|discharging|not-charging|full\"\n" );
1613 return -1;
1614}
1615
1616static int
1617do_battery_present( ControlClient client, char* args )
1618{
1619 if (args) {
1620 if (strcasecmp(args, "true") == 0) {
1621 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_PRESENT, 1);
1622 return 0;
1623 }
1624 if (strcasecmp(args, "false") == 0) {
1625 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_PRESENT, 0);
1626 return 0;
1627 }
1628 }
1629
1630 control_write( client, "KO: Usage: \"present true\" or \"present false\"\n" );
1631 return -1;
1632}
1633
1634static int
1635do_battery_health( ControlClient client, char* args )
1636{
1637 if (args) {
1638 if (strcasecmp(args, "unknown") == 0) {
1639 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_UNKNOWN);
1640 return 0;
1641 }
1642 if (strcasecmp(args, "good") == 0) {
1643 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_GOOD);
1644 return 0;
1645 }
1646 if (strcasecmp(args, "overheat") == 0) {
1647 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_OVERHEAT);
1648 return 0;
1649 }
1650 if (strcasecmp(args, "dead") == 0) {
1651 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_DEAD);
1652 return 0;
1653 }
1654 if (strcasecmp(args, "overvoltage") == 0) {
1655 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_OVERVOLTAGE);
1656 return 0;
1657 }
1658 if (strcasecmp(args, "failure") == 0) {
1659 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_UNSPEC_FAILURE);
1660 return 0;
1661 }
1662 }
1663
1664 control_write( client, "KO: Usage: \"health unknown|good|overheat|dead|overvoltage|failure\"\n" );
1665 return -1;
1666}
1667
1668static int
1669do_battery_capacity( ControlClient client, char* args )
1670{
1671 if (args) {
1672 int capacity;
1673
1674 if (sscanf(args, "%d", &capacity) == 1 && capacity >= 0 && capacity <= 100) {
1675 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_CAPACITY, capacity);
1676 return 0;
1677 }
1678 }
1679
1680 control_write( client, "KO: Usage: \"capacity <percentage>\"\n" );
1681 return -1;
1682}
1683
1684
1685static const CommandDefRec power_commands[] =
1686{
1687 { "display", "display battery and charger state",
1688 "display battery and charger state\r\n", NULL,
1689 do_power_display, NULL },
1690
1691 { "ac", "set AC charging state",
1692 "'ac on|off' allows you to set the AC charging state to on or off\r\n", NULL,
1693 do_ac_state, NULL },
1694
1695 { "status", "set battery status",
1696 "'status unknown|charging|discharging|not-charging|full' allows you to set battery status\r\n", NULL,
1697 do_battery_status, NULL },
1698
1699 { "present", "set battery present state",
1700 "'present true|false' allows you to set battery present state to true or false\r\n", NULL,
1701 do_battery_present, NULL },
1702
1703 { "health", "set battery health state",
1704 "'health unknown|good|overheat|dead|overvoltage|failure' allows you to set battery health state\r\n", NULL,
1705 do_battery_health, NULL },
1706
1707 { "capacity", "set battery capacity state",
1708 "'capacity <percentage>' allows you to set battery capacity to a value 0 - 100\r\n", NULL,
1709 do_battery_capacity, NULL },
1710
1711 { NULL, NULL, NULL, NULL, NULL, NULL }
1712};
1713
1714/********************************************************************************************/
1715/********************************************************************************************/
1716/***** ******/
1717/***** E V E N T C O M M A N D S ******/
1718/***** ******/
1719/********************************************************************************************/
1720/********************************************************************************************/
1721
1722
1723static int
1724do_event_send( ControlClient client, char* args )
1725{
1726 char* p;
1727
1728 if (!args) {
1729 control_write( client, "KO: Usage: event send <type>:<code>:<value> ...\r\n" );
1730 return -1;
1731 }
1732
1733 p = args;
1734 while (*p) {
1735 char* q;
1736 int type, code, value, ret;
1737
1738 p += strspn( args, " \t" ); /* skip spaces */
1739 if (*p == 0)
1740 break;
1741
1742 q = p + strcspn( p, " \t" );
1743
1744 if (q == p)
1745 break;
1746
1747 ret = android_event_from_str( p, &type, &code, &value );
1748 if (ret < 0) {
1749 if (ret == -1) {
1750 control_write( client,
1751 "KO: invalid event type in '%.*s', try 'event list types' for valid values\r\n",
1752 q-p, p );
1753 } else if (ret == -2) {
1754 control_write( client,
1755 "KO: invalid event code in '%.*s', try 'event list codes <type>' for valid values\r\n",
1756 q-p, p );
1757 } else {
1758 control_write( client,
1759 "KO: invalid event value in '%.*s', must be an integer\r\n",
1760 q-p, p);
1761 }
1762 return -1;
1763 }
1764
David 'Digit' Turner34f29742010-05-25 18:16:10 -07001765 user_event_generic( type, code, value );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001766 p = q;
1767 }
1768 return 0;
1769}
1770
1771static int
1772do_event_types( ControlClient client, char* args )
1773{
1774 int count = android_event_get_type_count();
1775 int nn;
1776
1777 control_write( client, "event <type> can be an integer or one of the following aliases\r\n" );
1778 for (nn = 0; nn < count; nn++) {
1779 char tmp[16];
1780 char* p = tmp;
1781 char* end = p + sizeof(tmp);
1782 int count2 = android_event_get_code_count( nn );;
1783
1784 p = android_event_bufprint_type_str( p, end, nn );
1785
1786 control_write( client, " %-8s", tmp );
1787 if (count2 > 0)
1788 control_write( client, " (%d code aliases)", count2 );
1789
1790 control_write( client, "\r\n" );
1791 }
1792 return 0;
1793}
1794
1795static int
1796do_event_codes( ControlClient client, char* args )
1797{
1798 int count;
1799 int nn, type, dummy;
1800
1801 if (!args) {
1802 control_write( client, "KO: argument missing, try 'event codes <type>'\r\n" );
1803 return -1;
1804 }
1805
1806 if ( android_event_from_str( args, &type, &dummy, &dummy ) < 0 ) {
1807 control_write( client, "KO: bad argument, see 'event types' for valid values\r\n" );
1808 return -1;
1809 }
1810
1811 count = android_event_get_code_count( type );
1812 if (count == 0) {
1813 control_write( client, "no code aliases defined for this type\r\n" );
1814 } else {
1815 control_write( client, "type '%s' accepts the following <code> aliases:\r\n",
1816 args );
1817 for (nn = 0; nn < count; nn++) {
1818 char temp[20], *p = temp, *end = p + sizeof(temp);
1819 android_event_bufprint_code_str( p, end, type, nn );
1820 control_write( client, " %-12s\r\n", temp );
1821 }
1822 }
1823
1824 return 0;
1825}
1826
1827static __inline__ int
1828utf8_next( unsigned char* *pp, unsigned char* end )
1829{
1830 unsigned char* p = *pp;
1831 int result = -1;
1832
1833 if (p < end) {
1834 int c= *p++;
1835 if (c >= 128) {
1836 if ((c & 0xe0) == 0xc0)
1837 c &= 0x1f;
1838 else if ((c & 0xf0) == 0xe0)
1839 c &= 0x0f;
1840 else
1841 c &= 0x07;
1842
1843 while (p < end && (p[0] & 0xc0) == 0x80) {
1844 c = (c << 6) | (p[0] & 0x3f);
1845 }
1846 }
1847 result = c;
1848 *pp = p;
1849 }
1850 return result;
1851}
1852
1853static int
1854do_event_text( ControlClient client, char* args )
1855{
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001856 AKeycodeBuffer keycodes;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001857 unsigned char* p = (unsigned char*) args;
1858 unsigned char* end = p + strlen(args);
1859 int textlen;
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001860 const AKeyCharmap* charmap;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001861
1862 if (!args) {
1863 control_write( client, "KO: argument missing, try 'event text <message>'\r\n" );
1864 return -1;
1865 }
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001866
1867 /* Get default charmap. */
Vladimir Chtchetkine43552dc2010-07-22 11:23:19 -07001868 charmap = android_get_default_charmap();
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001869 if (charmap == NULL) {
1870 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 -08001871 return -1;
1872 }
1873
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001874 keycodes.keycode_count = 0;
1875
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001876 /* un-secape message text into proper utf-8 (conversion happens in-site) */
1877 textlen = strlen((char*)p);
1878 textlen = sms_utf8_from_message_str( args, textlen, (unsigned char*)p, textlen );
1879 if (textlen < 0) {
1880 control_write( client, "message must be utf8 and can use the following escapes:\r\n"
1881 " \\n for a newline\r\n"
1882 " \\xNN where NN are two hexadecimal numbers\r\n"
1883 " \\uNNNN where NNNN are four hexadecimal numbers\r\n"
1884 " \\\\ to send a '\\' character\r\n\r\n"
1885 " anything else is an error\r\n"
1886 "KO: badly formatted text\r\n" );
1887 return -1;
1888 }
1889
1890 end = p + textlen;
1891 while (p < end) {
1892 int c = utf8_next( &p, end );
1893 if (c <= 0)
1894 break;
1895
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001896 android_charmap_reverse_map_unicode( NULL, (unsigned)c, 1, &keycodes );
1897 android_charmap_reverse_map_unicode( NULL, (unsigned)c, 0, &keycodes );
1898 android_keycodes_flush( &keycodes );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001899 }
1900
1901 return 0;
1902}
1903
1904static const CommandDefRec event_commands[] =
1905{
1906 { "send", "send a series of events to the kernel",
1907 "'event send <type>:<code>:<value> ...' allows your to send one or more hardware events\r\n"
1908 "to the Android kernel. you can use text names or integers for <type> and <code>\r\n", NULL,
1909 do_event_send, NULL },
1910
1911 { "types", "list all <type> aliases",
1912 "'event types' list all <type> string aliases supported by the 'event' subcommands\r\n",
1913 NULL, do_event_types, NULL },
1914
1915 { "codes", "list all <code> aliases for a given <type>",
1916 "'event codes <type>' lists all <code> string aliases for a given event <type>\r\n",
1917 NULL, do_event_codes, NULL },
1918
1919 { "text", "simulate keystrokes from a given text",
1920 "'event text <message>' allows you to simulate keypresses to generate a given text\r\n"
1921 "message. <message> must be an utf-8 string. Unicode points will be reverse-mapped\r\n"
1922 "according to the current device keyboard. unsupported characters will be discarded\r\n"
1923 "silently\r\n", NULL, do_event_text, NULL },
1924
1925 { NULL, NULL, NULL, NULL, NULL, NULL }
1926};
1927
1928
1929/********************************************************************************************/
1930/********************************************************************************************/
1931/***** ******/
1932/***** V M C O M M A N D S ******/
1933/***** ******/
1934/********************************************************************************************/
1935/********************************************************************************************/
1936
1937static int
1938do_avd_stop( ControlClient client, char* args )
1939{
1940 if (!vm_running) {
1941 control_write( client, "KO: virtual device already stopped\r\n" );
1942 return -1;
1943 }
1944 vm_stop(EXCP_INTERRUPT);
1945 return 0;
1946}
1947
1948static int
1949do_avd_start( ControlClient client, char* args )
1950{
1951 if (vm_running) {
1952 control_write( client, "KO: virtual device already running\r\n" );
1953 return -1;
1954 }
1955 vm_start();
1956 return 0;
1957}
1958
1959static int
1960do_avd_status( ControlClient client, char* args )
1961{
1962 control_write( client, "virtual device is %s\r\n", vm_running ? "running" : "stopped" );
1963 return 0;
1964}
1965
1966static int
1967do_avd_name( ControlClient client, char* args )
1968{
1969 control_write( client, "%s\r\n", avdInfo_getName(android_avdInfo) );
1970 return 0;
1971}
1972
1973static const CommandDefRec vm_commands[] =
1974{
1975 { "stop", "stop the virtual device",
1976 "'avd stop' stops the virtual device immediately, use 'avd start' to continue execution\r\n",
1977 NULL, do_avd_stop, NULL },
1978
1979 { "start", "start/restart the virtual device",
1980 "'avd start' will start or continue the virtual device, use 'avd stop' to stop it\r\n",
1981 NULL, do_avd_start, NULL },
1982
1983 { "status", "query virtual device status",
1984 "'avd status' will indicate wether the virtual device is running or not\r\n",
1985 NULL, do_avd_status, NULL },
1986
1987 { "name", "query virtual device name",
1988 "'avd name' will return the name of this virtual device\r\n",
1989 NULL, do_avd_name, NULL },
1990
1991 { NULL, NULL, NULL, NULL, NULL, NULL }
1992};
1993
1994/********************************************************************************************/
1995/********************************************************************************************/
1996/***** ******/
1997/***** G E O C O M M A N D S ******/
1998/***** ******/
1999/********************************************************************************************/
2000/********************************************************************************************/
2001
2002static int
2003do_geo_nmea( ControlClient client, char* args )
2004{
2005 if (!args) {
2006 control_write( client, "KO: NMEA sentence missing, try 'help geo nmea'\r\n" );
2007 return -1;
2008 }
2009 if (!android_gps_cs) {
2010 control_write( client, "KO: no GPS emulation in this virtual device\r\n" );
2011 return -1;
2012 }
2013 android_gps_send_nmea( args );
2014 return 0;
2015}
2016
2017static int
2018do_geo_fix( ControlClient client, char* args )
2019{
David 'Digit' Turnere92bc562010-09-07 06:21:25 -07002020#define MAX_GEO_PARAMS 5
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002021 char* p = args;
2022 int n_params = 0;
2023 double params[ MAX_GEO_PARAMS ];
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002024 int n_satellites = 1;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002025
2026 static int last_time = 0;
2027 static double last_altitude = 0.;
2028
2029 if (!p)
2030 p = "";
2031
2032 /* tokenize */
2033 while (*p) {
2034 char* end;
2035 double val = strtod( p, &end );
2036
2037 if (end == p) {
2038 control_write( client, "KO: argument '%s' is not a number\n", p );
2039 return -1;
2040 }
2041
2042 params[n_params++] = val;
2043 if (n_params >= MAX_GEO_PARAMS)
2044 break;
2045
2046 p = end;
2047 while (*p && (p[0] == ' ' || p[0] == '\t'))
2048 p += 1;
2049 }
2050
2051 /* sanity check */
2052 if (n_params < 2) {
2053 control_write( client, "KO: not enough arguments: see 'help geo fix' for details\r\n" );
2054 return -1;
2055 }
2056
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002057 /* check number of satellites, must be between 1 and 12 */
2058 if (n_params >= 4) {
2059 n_satellites = (int) params[4];
2060 if (n_satellites != params[4] || n_satellites < 1 || n_satellites > 12) {
2061 control_write( client, "KO: invalid number of satellites. Must be an integer between 1 and 12\r\n");
2062 return -1;
2063 }
2064 }
2065
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002066 /* generate an NMEA sentence for this fix */
2067 {
2068 STRALLOC_DEFINE(s);
2069 double val;
2070 int deg, min;
2071 char hemi;
2072
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002073 /* format overview:
2074 * time of fix 123519 12:35:19 UTC
2075 * latitude 4807.038 48 degrees, 07.038 minutes
2076 * north/south N or S
2077 * longitude 01131.000 11 degrees, 31. minutes
2078 * east/west E or W
2079 * fix quality 1 standard GPS fix
2080 * satellites 1 to 12 number of satellites being tracked
2081 * HDOP <dontcare> horizontal dilution
2082 * altitude 546. altitude above sea-level
2083 * altitude units M to indicate meters
2084 * diff <dontcare> height of sea-level above ellipsoid
2085 * diff units M to indicate meters (should be <dontcare>)
2086 * dgps age <dontcare> time in seconds since last DGPS fix
2087 * dgps sid <dontcare> DGPS station id
2088 */
2089
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002090 /* first, the time */
2091 stralloc_add_format( s, "$GPGGA,%06d", last_time );
2092 last_time ++;
2093
2094 /* then the latitude */
2095 hemi = 'N';
2096 val = params[1];
2097 if (val < 0) {
2098 hemi = 'S';
2099 val = -val;
2100 }
2101 deg = (int) val;
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002102 val = 60*(val - deg);
2103 min = (int) val;
2104 val = 1000*(val - min);
2105 stralloc_add_format( s, ",%02d%02d.%04d,%c", deg, min, (int)val, hemi );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002106
2107 /* the longitude */
2108 hemi = 'E';
2109 val = params[0];
2110 if (val < 0) {
2111 hemi = 'W';
2112 val = -val;
2113 }
2114 deg = (int) val;
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002115 val = 60*(val - deg);
2116 min = (int) val;
2117 val = 1000*(val - min);
2118 stralloc_add_format( s, ",%02d%02d.%04d,%c", deg, min, (int)val, hemi );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002119
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002120 /* bogus fix quality, satellite count and dilution */
2121 stralloc_add_format( s, ",1,%02d,", n_satellites );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002122
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002123 /* optional altitude + bogus diff */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002124 if (n_params >= 3) {
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002125 stralloc_add_format( s, ",%.1g,M,0.,M", params[2] );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002126 last_altitude = params[2];
2127 } else {
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002128 stralloc_add_str( s, ",,,," );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002129 }
2130 /* bogus rest and checksum */
2131 stralloc_add_str( s, ",,,*47" );
2132
2133 /* send it, then free */
2134 android_gps_send_nmea( stralloc_cstr(s) );
2135 stralloc_reset( s );
2136 }
2137 return 0;
2138}
2139
2140static const CommandDefRec geo_commands[] =
2141{
2142 { "nmea", "send an GPS NMEA sentence",
2143 "'geo nema <sentence>' sends a NMEA 0183 sentence to the emulated device, as\r\n"
2144 "if it came from an emulated GPS modem. <sentence> must begin with '$GP'. only\r\n"
2145 "'$GPGGA' and '$GPRCM' sentences are supported at the moment.\r\n",
2146 NULL, do_geo_nmea, NULL },
2147
2148 { "fix", "send a simple GPS fix",
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002149 "'geo fix <longitude> <latitude> [<altitude> [<satellites>]]' allows you to send a\r\n"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002150 "simple GPS fix to the emulated system. the parameters are:\r\n\r\n"
2151 " <longitude> longitude, in decimal degrees\r\n"
2152 " <latitude> latitude, in decimal degrees\r\n"
2153 " <altitude> optional altitude in meters\r\n"
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002154 " <satellites> number of satellites being tracked (1-12)\r\n"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002155 "\r\n",
2156 NULL, do_geo_fix, NULL },
2157
2158 { NULL, NULL, NULL, NULL, NULL, NULL }
2159};
2160
2161
2162/********************************************************************************************/
2163/********************************************************************************************/
2164/***** ******/
2165/***** M A I N C O M M A N D S ******/
2166/***** ******/
2167/********************************************************************************************/
2168/********************************************************************************************/
2169
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002170static int
2171do_window_scale( ControlClient client, char* args )
2172{
2173 double scale;
2174 int is_dpi = 0;
2175 char* end;
2176
2177 if (!args) {
2178 control_write( client, "KO: argument missing, try 'window scale <scale>'\r\n" );
2179 return -1;
2180 }
2181
2182 scale = strtol( args, &end, 10 );
2183 if (end > args && !memcmp( end, "dpi", 4 )) {
2184 is_dpi = 1;
2185 }
2186 else {
2187 scale = strtod( args, &end );
2188 if (end == args || end[0]) {
2189 control_write( client, "KO: argument <scale> must be a real number, or an integer followed by 'dpi'\r\n" );
2190 return -1;
2191 }
2192 }
2193
Vladimir Chtchetkine2fa51732010-07-16 11:19:48 -07002194 android_ui_set_window_scale( scale, is_dpi );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002195 return 0;
2196}
2197
2198static const CommandDefRec window_commands[] =
2199{
2200 { "scale", "change the window scale",
2201 "'window scale <scale>' allows you to change the scale of the emulator window at runtime\r\n"
2202 "<scale> must be either a real number between 0.1 and 3.0, or an integer followed by\r\n"
2203 "the 'dpi' prefix (as in '120dpi')\r\n",
2204 NULL, do_window_scale, NULL },
2205
2206 { NULL, NULL, NULL, NULL, NULL, NULL }
2207};
2208
2209/********************************************************************************************/
2210/********************************************************************************************/
2211/***** ******/
David 'Digit' Turnere92bc562010-09-07 06:21:25 -07002212/***** Q E M U C O M M A N D S ******/
2213/***** ******/
2214/********************************************************************************************/
2215/********************************************************************************************/
2216
2217static int
2218do_qemu_monitor( ControlClient client, char* args )
2219{
2220 char socketname[32];
2221 int fd;
2222 CharDriverState* cs;
2223
2224 if (args != NULL) {
2225 control_write( client, "KO: no argument for 'qemu monitor'\r\n" );
2226 return -1;
2227 }
2228 /* Detach the client socket, and re-attach it to a monitor */
2229 fd = control_client_detach(client);
2230 snprintf(socketname, sizeof socketname, "tcp:socket=%d", fd);
2231 cs = qemu_chr_open("monitor", socketname, NULL);
2232 if (cs == NULL) {
2233 control_client_reattach(client, fd);
2234 control_write( client, "KO: internal error: could not detach from console !\r\n" );
2235 return -1;
2236 }
2237 monitor_init(cs, MONITOR_USE_READLINE|MONITOR_QUIT_DOESNT_EXIT);
2238 control_client_destroy(client);
2239 return 0;
2240}
2241
2242static const CommandDefRec qemu_commands[] =
2243{
2244 { "monitor", "enter QEMU monitor",
2245 "Enter the QEMU virtual machine monitor\r\n",
2246 NULL, do_qemu_monitor, NULL },
2247
2248 { NULL, NULL, NULL, NULL, NULL, NULL }
2249};
2250
2251
2252/********************************************************************************************/
2253/********************************************************************************************/
2254/***** ******/
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002255/***** M A I N C O M M A N D S ******/
2256/***** ******/
2257/********************************************************************************************/
2258/********************************************************************************************/
2259
2260static int
2261do_kill( ControlClient client, char* args )
2262{
2263 control_write( client, "OK: killing emulator, bye bye\r\n" );
2264 exit(0);
2265}
2266
2267static const CommandDefRec main_commands[] =
2268{
2269 { "help|h|?", "print a list of commands", NULL, NULL, do_help, NULL },
2270
2271 { "event", "simulate hardware events",
2272 "allows you to send fake hardware events to the kernel\r\n", NULL,
2273 NULL, event_commands },
2274
2275 { "geo", "Geo-location commands",
2276 "allows you to change Geo-related settings, or to send GPS NMEA sentences\r\n", NULL,
2277 NULL, geo_commands },
2278
2279 { "gsm", "GSM related commands",
2280 "allows you to change GSM-related settings, or to make a new inbound phone call\r\n", NULL,
2281 NULL, gsm_commands },
2282
Jaime Lopez1a000852010-07-21 18:03:58 -07002283 { "cdma", "CDMA related commands",
2284 "allows you to change CDMA-related settings\r\n", NULL,
2285 NULL, cdma_commands },
2286
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002287 { "kill", "kill the emulator instance", NULL, NULL,
2288 do_kill, NULL },
2289
2290 { "network", "manage network settings",
2291 "allows you to manage the settings related to the network data connection of the\r\n"
2292 "emulated device.\r\n", NULL,
2293 NULL, network_commands },
2294
2295 { "power", "power related commands",
2296 "allows to change battery and AC power status\r\n", NULL,
2297 NULL, power_commands },
2298
2299 { "quit|exit", "quit control session", NULL, NULL,
2300 do_quit, NULL },
2301
2302 { "redir", "manage port redirections",
2303 "allows you to add, list and remove UDP and/or PORT redirection from the host to the device\r\n"
2304 "as an example, 'redir tcp:5000:6000' will route any packet sent to the host's TCP port 5000\r\n"
2305 "to TCP port 6000 of the emulated device\r\n", NULL,
2306 NULL, redir_commands },
2307
2308 { "sms", "SMS related commands",
2309 "allows you to simulate an inbound SMS\r\n", NULL,
2310 NULL, sms_commands },
2311
2312 { "avd", "manager virtual device state",
2313 "allows to change (e.g. start/stop) the virtual device state\r\n", NULL,
2314 NULL, vm_commands },
2315
2316 { "window", "manage emulator window",
2317 "allows you to modify the emulator window\r\n", NULL,
2318 NULL, window_commands },
2319
David 'Digit' Turnere92bc562010-09-07 06:21:25 -07002320 { "qemu", "QEMU-specific commands",
2321 "allows to connect to the QEMU virtual machine monitor\r\n", NULL,
2322 NULL, qemu_commands },
2323
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002324 { NULL, NULL, NULL, NULL, NULL, NULL }
2325};
2326
2327
2328static ControlGlobalRec _g_global;
2329
2330int
2331control_console_start( int port )
2332{
2333 return control_global_init( &_g_global, port );
2334}