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