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