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