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