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