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