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