blob: f4273f4a1ee8b67b7ba38690de9e572f571c7a05 [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 Chtchetkinee95660a2010-12-20 08:28:03 -080053#include "android/display-core.h"
Vladimir Chtchetkine94a2fba2011-01-31 10:49:06 -080054#include "android/protocol/fb-updates-proxy.h"
Vladimir Chtchetkine250b2e02011-01-28 10:56:16 -080055#include "android/protocol/user-events-impl.h"
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -080056#include "android/protocol/ui-commands-api.h"
57#include "android/protocol/core-commands-impl.h"
58#include "android/protocol/ui-commands-proxy.h"
Vladimir Chtchetkine85276802011-01-31 15:18:45 -080059#include "android/protocol/attach-ui-proxy.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080060
61#if defined(CONFIG_SLIRP)
62#include "libslirp.h"
63#endif
64
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080065#define DEBUG 1
66
67#if 1
68# define D_ACTIVE VERBOSE_CHECK(console)
69#else
70# define D_ACTIVE DEBUG
71#endif
72
73#if DEBUG
74# define D(x) do { if (D_ACTIVE) ( printf x , fflush(stdout) ); } while (0)
75#else
76# define D(x) do{}while(0)
77#endif
78
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080079typedef struct ControlGlobalRec_* ControlGlobal;
80
81typedef struct ControlClientRec_* ControlClient;
82
83typedef struct {
84 int host_port;
85 int host_udp;
86 unsigned int guest_ip;
87 int guest_port;
88} RedirRec, *Redir;
89
90
David 'Digit' Turnere92bc562010-09-07 06:21:25 -070091typedef int Socket;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080092
93typedef struct ControlClientRec_
94{
95 struct ControlClientRec_* next; /* next client in list */
96 Socket sock; /* socket used for communication */
97 ControlGlobal global;
98 char finished;
99 char buff[ 4096 ];
100 int buff_len;
101
102} ControlClientRec;
103
104
105typedef struct ControlGlobalRec_
106{
107 /* listening socket */
108 Socket listen_fd;
109
110 /* the list of current clients */
111 ControlClient clients;
112
113 /* the list of redirections currently active */
114 Redir redirs;
115 int num_redirs;
116 int max_redirs;
117
118} ControlGlobalRec;
119
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800120#ifdef CONFIG_STANDALONE_CORE
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -0800121/* UI client currently attached to the core. */
122ControlClient attached_ui_client = NULL;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800123
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -0800124/* User events service client. */
125ControlClient user_events_client = NULL;
126
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -0800127/* UI control service client (UI -> Core). */
128ControlClient ui_core_ctl_client = NULL;
129
130/* UI control service (UI -> Core. */
131// CoreUICtl* ui_core_ctl = NULL;
132
133/* UI control service client (Core-> UI). */
134ControlClient core_ui_ctl_client = NULL;
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800135#endif // CONFIG_STANDALONE_CORE
136
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800137static int
138control_global_add_redir( ControlGlobal global,
139 int host_port,
140 int host_udp,
141 unsigned int guest_ip,
142 int guest_port )
143{
144 Redir redir;
145
146 if (global->num_redirs >= global->max_redirs)
147 {
148 int old_max = global->max_redirs;
149 int new_max = old_max + (old_max >> 1) + 4;
150
151 Redir new_redirs = realloc( global->redirs, new_max*sizeof(global->redirs[0]) );
152 if (new_redirs == NULL)
153 return -1;
154
155 global->redirs = new_redirs;
156 global->max_redirs = new_max;
157 }
158
159 redir = &global->redirs[ global->num_redirs++ ];
160
161 redir->host_port = host_port;
162 redir->host_udp = host_udp;
163 redir->guest_ip = guest_ip;
164 redir->guest_port = guest_port;
165
166 return 0;
167}
168
169static int
170control_global_del_redir( ControlGlobal global,
171 int host_port,
172 int host_udp )
173{
174 int nn;
175
176 for (nn = 0; nn < global->num_redirs; nn++)
177 {
178 Redir redir = &global->redirs[nn];
179
180 if ( redir->host_port == host_port &&
181 redir->host_udp == host_udp )
182 {
183 memmove( redir, redir + 1, ((global->num_redirs - nn)-1)*sizeof(*redir) );
184 global->num_redirs -= 1;
185 return 0;
186 }
187 }
188 /* we didn't find it */
189 return -1;
190}
191
David 'Digit' Turnere92bc562010-09-07 06:21:25 -0700192/* Detach the socket descriptor from a given ControlClient
193 * and return its value. This is useful either when destroying
194 * the client, or redirecting the socket to another service.
195 *
196 * NOTE: this does not close the socket.
197 */
198static int
199control_client_detach( ControlClient client )
200{
201 int result;
202
203 if (client->sock < 0)
204 return -1;
205
206 qemu_set_fd_handler( client->sock, NULL, NULL, NULL );
207 result = client->sock;
208 client->sock = -1;
209
210 return result;
211}
212
213static void control_client_read( void* _client ); /* forward */
214
215/* Reattach a control client to a given socket.
216 * Return the old socket descriptor for the client.
217 */
218static int
219control_client_reattach( ControlClient client, int fd )
220{
221 int result = control_client_detach(client);
222 client->sock = fd;
223 qemu_set_fd_handler( fd, control_client_read, NULL, client );
224 return result;
225}
226
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800227static void
228control_client_destroy( ControlClient client )
229{
230 ControlGlobal global = client->global;
231 ControlClient *pnode = &global->clients;
David 'Digit' Turnere92bc562010-09-07 06:21:25 -0700232 int sock;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800233
234 D(( "destroying control client %p\n", client ));
235
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800236#ifdef CONFIG_STANDALONE_CORE
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -0800237 if (client == attached_ui_client) {
Vladimir Chtchetkine85276802011-01-31 15:18:45 -0800238 attachUiProxy_destroy();
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -0800239 attached_ui_client = NULL;
240 }
241
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -0800242 if (client == user_events_client) {
Vladimir Chtchetkine250b2e02011-01-28 10:56:16 -0800243 userEventsImpl_destroy();
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -0800244 user_events_client = NULL;
245 }
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -0800246
247 if (client == ui_core_ctl_client) {
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800248 coreCmdImpl_destroy();
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -0800249 ui_core_ctl_client = NULL;
250 }
251
252 if (client == core_ui_ctl_client) {
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800253 uiCmdProxy_destroy();
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -0800254 core_ui_ctl_client = NULL;
255 }
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800256#endif // CONFIG_STANDALONE_CORE
257
David 'Digit' Turnere92bc562010-09-07 06:21:25 -0700258 sock = control_client_detach( client );
259 if (sock >= 0)
260 socket_close(sock);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800261
262 for ( ;; ) {
263 ControlClient node = *pnode;
264 if ( node == NULL )
265 break;
266 if ( node == client ) {
267 *pnode = node->next;
268 node->next = NULL;
269 break;
270 }
271 pnode = &node->next;
272 }
273
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800274 free( client );
275}
276
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800277
278
279static void control_control_write( ControlClient client, const char* buff, int len )
280{
281 int ret;
282
283 if (len < 0)
284 len = strlen(buff);
285
286 while (len > 0) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800287 ret = socket_send( client->sock, buff, len);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800288 if (ret < 0) {
David 'Digit' Turnerce0f4b02010-03-25 11:11:29 -0700289 if (errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800290 return;
291 } else {
292 buff += ret;
293 len -= ret;
294 }
295 }
296}
297
Ot ten Thije2ff39a32010-10-06 17:48:15 +0100298static int control_vwrite( ControlClient client, const char* format, va_list args )
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800299{
300 static char temp[1024];
Ot ten Thije2ff39a32010-10-06 17:48:15 +0100301 int ret = vsnprintf( temp, sizeof(temp), format, args );
302 temp[ sizeof(temp)-1 ] = 0;
303 control_control_write( client, temp, -1 );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800304
Ot ten Thije2ff39a32010-10-06 17:48:15 +0100305 return ret;
306}
307
308static int control_write( ControlClient client, const char* format, ... )
309{
310 int ret;
311 va_list args;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800312 va_start(args, format);
Ot ten Thije2ff39a32010-10-06 17:48:15 +0100313 ret = control_vwrite(client, format, args);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800314 va_end(args);
315
Ot ten Thije2ff39a32010-10-06 17:48:15 +0100316 return ret;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800317}
318
319
320static ControlClient
321control_client_create( Socket socket,
322 ControlGlobal global )
323{
324 ControlClient client = calloc( sizeof(*client), 1 );
325
326 if (client) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800327 socket_set_nodelay( socket );
328 socket_set_nonblock( socket );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800329 client->finished = 0;
330 client->global = global;
331 client->sock = socket;
332 client->next = global->clients;
333 global->clients = client;
334
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800335 qemu_set_fd_handler( socket, control_client_read, NULL, client );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800336 }
337 return client;
338}
339
340typedef const struct CommandDefRec_ *CommandDef;
341
342typedef struct CommandDefRec_ {
343 const char* names;
344 const char* abstract;
345 const char* description;
346 void (*descriptor)( ControlClient client );
347 int (*handler)( ControlClient client, char* args );
348 CommandDef subcommands; /* if handler is NULL */
349
350} CommandDefRec;
351
352static const CommandDefRec main_commands[]; /* forward */
353
354static CommandDef
355find_command( char* input, CommandDef commands, char* *pend, char* *pargs )
356{
357 int nn;
358 char* args = strchr(input, ' ');
359
360 if (args != NULL) {
361 while (*args == ' ')
362 args++;
363
364 if (args[0] == 0)
365 args = NULL;
366 }
367
368 for (nn = 0; commands[nn].names != NULL; nn++)
369 {
370 const char* name = commands[nn].names;
371 const char* sep;
372
373 do {
374 int len, c;
375
376 sep = strchr( name, '|' );
377 if (sep)
378 len = sep - name;
379 else
380 len = strlen(name);
381
382 c = input[len];
383 if ( !memcmp( name, input, len ) && (c == ' ' || c == 0) ) {
384 *pend = input + len;
385 *pargs = args;
386 return &commands[nn];
387 }
388
389 if (sep)
390 name = sep + 1;
391
392 } while (sep != NULL && *name);
393 }
394 /* NOTE: don't touch *pend and *pargs if no command is found */
395 return NULL;
396}
397
398static void
399dump_help( ControlClient client,
400 CommandDef cmd,
401 const char* prefix )
402{
403 if (cmd->description) {
404 control_write( client, "%s", cmd->description );
405 } else if (cmd->descriptor) {
406 cmd->descriptor( client );
407 } else
408 control_write( client, "%s\r\n", cmd->abstract );
409
410 if (cmd->subcommands) {
411 cmd = cmd->subcommands;
412 control_write( client, "\r\navailable sub-commands:\r\n" );
413 for ( ; cmd->names != NULL; cmd++ ) {
414 control_write( client, " %s %-15s %s\r\n", prefix, cmd->names, cmd->abstract );
415 }
416 control_write( client, "\r\n" );
417 }
418}
419
420static void
421control_client_do_command( ControlClient client )
422{
423 char* line = client->buff;
424 char* args = NULL;
425 CommandDef commands = main_commands;
426 char* cmdend = client->buff;
427 CommandDef cmd = find_command( line, commands, &cmdend, &args );
428
429 if (cmd == NULL) {
430 control_write( client, "KO: unknown command, try 'help'\r\n" );
431 return;
432 }
433
434 for (;;) {
435 CommandDef subcmd;
436
437 if (cmd->handler) {
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -0800438 if ( !cmd->handler( client, args ) ) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800439 control_write( client, "OK\r\n" );
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -0800440 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800441 break;
442 }
443
444 /* no handler means we should have sub-commands */
445 if (cmd->subcommands == NULL) {
446 control_write( client, "KO: internal error: buggy command table for '%.*s'\r\n",
447 cmdend - client->buff, client->buff );
448 break;
449 }
450
451 /* we need a sub-command here */
452 if ( !args ) {
453 dump_help( client, cmd, "" );
454 control_write( client, "KO: missing sub-command\r\n" );
455 break;
456 }
457
458 line = args;
459 commands = cmd->subcommands;
460 subcmd = find_command( line, commands, &cmdend, &args );
461 if (subcmd == NULL) {
462 dump_help( client, cmd, "" );
463 control_write( client, "KO: bad sub-command\r\n" );
464 break;
465 }
466 cmd = subcmd;
467 }
468}
469
470/* implement the 'help' command */
471static int
472do_help( ControlClient client, char* args )
473{
474 char* line;
475 char* start = args;
476 char* end = start;
477 CommandDef cmd = main_commands;
478
479 /* without arguments, simply dump all commands */
480 if (args == NULL) {
481 control_write( client, "Android console command help:\r\n\r\n" );
482 for ( ; cmd->names != NULL; cmd++ ) {
483 control_write( client, " %-15s %s\r\n", cmd->names, cmd->abstract );
484 }
485 control_write( client, "\r\ntry 'help <command>' for command-specific help\r\n" );
486 return 0;
487 }
488
489 /* with an argument, find the corresponding command */
490 for (;;) {
491 CommandDef subcmd;
492
493 line = args;
494 subcmd = find_command( line, cmd, &end, &args );
495 if (subcmd == NULL) {
496 control_write( client, "try one of these instead:\r\n\r\n" );
497 for ( ; cmd->names != NULL; cmd++ ) {
498 control_write( client, " %.*s %s\r\n",
499 end - start, start, cmd->names );
500 }
501 control_write( client, "\r\nKO: unknown command\r\n" );
502 return -1;
503 }
504
505 if ( !args || !subcmd->subcommands ) {
506 dump_help( client, subcmd, start );
507 return 0;
508 }
509 cmd = subcmd->subcommands;
510 }
511}
512
513
514static void
515control_client_read_byte( ControlClient client, unsigned char ch )
516{
517 if (ch == '\r')
518 {
519 /* filter them out */
520 }
521 else if (ch == '\n')
522 {
523 client->buff[ client->buff_len ] = 0;
524 control_client_do_command( client );
525 if (client->finished)
526 return;
527
528 client->buff_len = 0;
529 }
530 else
531 {
532 if (client->buff_len >= sizeof(client->buff)-1)
533 client->buff_len = 0;
534
535 client->buff[ client->buff_len++ ] = ch;
536 }
537}
538
539static void
540control_client_read( void* _client )
541{
542 ControlClient client = _client;
543 unsigned char buf[4096];
544 int size;
545
546 D(( "in control_client read: " ));
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800547 size = socket_recv( client->sock, buf, sizeof(buf) );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800548 if (size < 0) {
549 D(( "size < 0, exiting with %d: %s\n", errno, errno_str ));
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -0800550 if (errno != EWOULDBLOCK && errno != EAGAIN)
551 control_client_destroy( client );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800552 return;
553 }
554
555 if (size == 0) {
556 /* end of connection */
557 D(( "end of connection detected !!\n" ));
558 control_client_destroy( client );
559 }
560 else {
561 int nn;
562#ifdef _WIN32
563# if DEBUG
564 char temp[16];
565 int count = size > sizeof(temp)-1 ? sizeof(temp)-1 : size;
566 for (nn = 0; nn < count; nn++) {
567 int c = buf[nn];
568 if (c == '\n')
569 temp[nn] = '!';
570 else if (c < 32)
571 temp[nn] = '.';
572 else
573 temp[nn] = (char)c;
574 }
575 temp[nn] = 0;
576 D(( "received %d bytes: %s\n", size, temp ));
577# endif
578#else
579 D(( "received %.*s\n", size, buf ));
580#endif
581 for (nn = 0; nn < size; nn++) {
582 control_client_read_byte( client, buf[nn] );
583 if (client->finished) {
584 control_client_destroy(client);
585 return;
586 }
587 }
588 }
589}
590
591
592/* this function is called on each new client connection */
593static void
594control_global_accept( void* _global )
595{
596 ControlGlobal global = _global;
597 ControlClient client;
598 Socket fd;
599
David 'Digit' Turner80bc5c82010-10-20 19:04:51 +0200600 D(( "control_global_accept: just in (fd=%d)\n", global->listen_fd ));
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800601
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800602 for(;;) {
603 fd = socket_accept( global->listen_fd, NULL );
604 if (fd < 0 && errno != EINTR) {
605 D(( "problem in accept: %d: %s\n", errno, errno_str ));
606 perror("accept");
607 return;
608 } else if (fd >= 0) {
609 break;
610 }
611 D(( "relooping in accept()\n" ));
612 }
613
614 socket_set_xreuseaddr( fd );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800615
616 D(( "control_global_accept: creating new client\n" ));
617 client = control_client_create( fd, global );
618 if (client) {
619 D(( "control_global_accept: new client %p\n", client ));
620 control_write( client, "Android Console: type 'help' for a list of commands\r\n" );
621 control_write( client, "OK\r\n" );
622 }
623}
624
625
626static int
627control_global_init( ControlGlobal global,
628 int control_port )
629{
630 Socket fd;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800631 int ret;
632 SockAddress sockaddr;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800633
634 memset( global, 0, sizeof(*global) );
635
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800636 fd = socket_create_inet( SOCKET_STREAM );
637 if (fd < 0) {
638 perror("socket");
639 return -1;
640 }
641
642 socket_set_xreuseaddr( fd );
643
644 sock_address_init_inet( &sockaddr, SOCK_ADDRESS_INET_LOOPBACK, control_port );
645
646 ret = socket_bind(fd, &sockaddr );
647 if (ret < 0) {
648 perror("bind");
649 socket_close( fd );
650 return -1;
651 }
652
653 ret = socket_listen(fd, 0);
654 if (ret < 0) {
655 perror("listen");
656 socket_close( fd );
657 return -1;
658 }
659
660 socket_set_nonblock(fd);
661
662 global->listen_fd = fd;
663
664 qemu_set_fd_handler( fd, control_global_accept, NULL, global );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800665 return 0;
666}
667
668
669
670static int
671do_quit( ControlClient client, char* args )
672{
673 client->finished = 1;
674 return -1;
675}
676
677/********************************************************************************************/
678/********************************************************************************************/
679/***** ******/
680/***** N E T W O R K S E T T I N G S ******/
681/***** ******/
682/********************************************************************************************/
683/********************************************************************************************/
684
685static int
686do_network_status( ControlClient client, char* args )
687{
688 control_write( client, "Current network status:\r\n" );
689
690 control_write( client, " download speed: %8d bits/s (%.1f KB/s)\r\n",
691 (long)qemu_net_download_speed, qemu_net_download_speed/8192. );
692
693 control_write( client, " upload speed: %8d bits/s (%.1f KB/s)\r\n",
694 (long)qemu_net_upload_speed, qemu_net_upload_speed/8192. );
695
696 control_write( client, " minimum latency: %ld ms\r\n", qemu_net_min_latency );
697 control_write( client, " maximum latency: %ld ms\r\n", qemu_net_max_latency );
698 return 0;
699}
700
701static void
702dump_network_speeds( ControlClient client )
703{
704 const NetworkSpeed* speed = android_netspeeds;
705 const char* const format = " %-8s %s\r\n";
706 for ( ; speed->name; speed++ ) {
707 control_write( client, format, speed->name, speed->display );
708 }
709 control_write( client, format, "<num>", "selects both upload and download speed" );
710 control_write( client, format, "<up>:<down>", "select individual upload/download speeds" );
711}
712
713
714static int
715do_network_speed( ControlClient client, char* args )
716{
717 if ( !args ) {
718 control_write( client, "KO: missing <speed> argument, see 'help network speed'\r\n" );
719 return -1;
720 }
721 if ( android_parse_network_speed( args ) < 0 ) {
722 control_write( client, "KO: invalid <speed> argument, see 'help network speed' for valid values\r\n" );
723 return -1;
724 }
725
726 netshaper_set_rate( slirp_shaper_in, qemu_net_download_speed );
727 netshaper_set_rate( slirp_shaper_out, qemu_net_upload_speed );
728
729 if (android_modem) {
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -0700730 amodem_set_data_network_type( android_modem,
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800731 android_parse_network_type( args ) );
732 }
733 return 0;
734}
735
736static void
737describe_network_speed( ControlClient client )
738{
739 control_write( client,
740 "'network speed <speed>' allows you to dynamically change the speed of the emulated\r\n"
741 "network on the device, where <speed> is one of the following:\r\n\r\n" );
742 dump_network_speeds( client );
743}
744
745static int
746do_network_delay( ControlClient client, char* args )
747{
748 if ( !args ) {
749 control_write( client, "KO: missing <delay> argument, see 'help network delay'\r\n" );
750 return -1;
751 }
752 if ( android_parse_network_latency( args ) < 0 ) {
753 control_write( client, "KO: invalid <delay> argument, see 'help network delay' for valid values\r\n" );
754 return -1;
755 }
756 netdelay_set_latency( slirp_delay_in, qemu_net_min_latency, qemu_net_max_latency );
757 return 0;
758}
759
760static void
761describe_network_delay( ControlClient client )
762{
763 control_write( client,
764 "'network delay <latency>' allows you to dynamically change the latency of the emulated\r\n"
765 "network on the device, where <latency> is one of the following:\r\n\r\n" );
766 /* XXX: TODO */
767}
768
769static int
770do_network_capture_start( ControlClient client, char* args )
771{
772 if ( !args ) {
773 control_write( client, "KO: missing <file> argument, see 'help network capture start'\r\n" );
774 return -1;
775 }
776 if ( qemu_tcpdump_start(args) < 0) {
777 control_write( client, "KO: could not start capture: %s", strerror(errno) );
778 return -1;
779 }
780 return 0;
781}
782
783static int
784do_network_capture_stop( ControlClient client, char* args )
785{
786 /* no need to return an error here */
787 qemu_tcpdump_stop();
788 return 0;
789}
790
791static const CommandDefRec network_capture_commands[] =
792{
793 { "start", "start network capture",
794 "'network capture start <file>' starts a new capture of network packets\r\n"
795 "into a specific <file>. This will stop any capture already in progress.\r\n"
796 "the capture file can later be analyzed by tools like WireShark. It uses\r\n"
797 "the libpcap file format.\r\n\r\n"
798 "you can stop the capture anytime with 'network capture stop'\r\n", NULL,
799 do_network_capture_start, NULL },
800
801 { "stop", "stop network capture",
802 "'network capture stop' stops a currently running packet capture, if any.\r\n"
803 "you can start one with 'network capture start <file>'\r\n", NULL,
804 do_network_capture_stop, NULL },
805
806 { NULL, NULL, NULL, NULL, NULL, NULL }
807};
808
809static const CommandDefRec network_commands[] =
810{
811 { "status", "dump network status", NULL, NULL,
812 do_network_status, NULL },
813
814 { "speed", "change network speed", NULL, describe_network_speed,
815 do_network_speed, NULL },
816
817 { "delay", "change network latency", NULL, describe_network_delay,
818 do_network_delay, NULL },
819
820 { "capture", "dump network packets to file",
821 "allows to start/stop capture of network packets to a file for later analysis\r\n", NULL,
822 NULL, network_capture_commands },
823
824 { NULL, NULL, NULL, NULL, NULL, NULL }
825};
826
827/********************************************************************************************/
828/********************************************************************************************/
829/***** ******/
830/***** P O R T R E D I R E C T I O N S ******/
831/***** ******/
832/********************************************************************************************/
833/********************************************************************************************/
834
835static int
836do_redir_list( ControlClient client, char* args )
837{
838 ControlGlobal global = client->global;
839
840 if (global->num_redirs == 0)
841 control_write( client, "no active redirections\r\n" );
842 else {
843 int nn;
844 for (nn = 0; nn < global->num_redirs; nn++) {
845 Redir redir = &global->redirs[nn];
846 control_write( client, "%s:%-5d => %-5d\r\n",
847 redir->host_udp ? "udp" : "tcp",
848 redir->host_port,
849 redir->guest_port );
850 }
851 }
852 return 0;
853}
854
855/* parse a protocol:port specification */
856static int
857redir_parse_proto_port( char* args, int *pport, int *pproto )
858{
859 int proto = -1;
860 int len = 0;
861 char* end;
862
863 if ( !memcmp( args, "tcp:", 4 ) ) {
864 proto = 0;
865 len = 4;
866 }
867 else if ( !memcmp( args, "udp:", 4 ) ) {
868 proto = 1;
869 len = 4;
870 }
871 else
872 return 0;
873
874 args += len;
875 *pproto = proto;
876 *pport = strtol( args, &end, 10 );
877 if (end == args)
878 return 0;
879
880 len += end - args;
881 return len;
882}
883
884static int
885redir_parse_guest_port( char* arg, int *pport )
886{
887 char* end;
888
889 *pport = strtoul( arg, &end, 10 );
890 if (end == arg)
891 return 0;
892
893 return end - arg;
894}
895
896static Redir
897redir_find( ControlGlobal global, int port, int isudp )
898{
899 int nn;
900
901 for (nn = 0; nn < global->num_redirs; nn++) {
902 Redir redir = &global->redirs[nn];
903
904 if (redir->host_port == port && redir->host_udp == isudp)
905 return redir;
906 }
907 return NULL;
908}
909
910
911static int
912do_redir_add( ControlClient client, char* args )
913{
914 int len, host_proto, host_port, guest_port;
915 uint32_t guest_ip;
916 Redir redir;
917
918 if ( !args )
919 goto BadFormat;
920
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700921 if (!slirp_is_inited()) {
922 control_write( client, "KO: network emulation disabled\r\n");
923 return -1;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800924 }
925
926 len = redir_parse_proto_port( args, &host_port, &host_proto );
927 if (len == 0 || args[len] != ':')
928 goto BadFormat;
929
930 args += len + 1;
931 len = redir_parse_guest_port( args, &guest_port );
932 if (len == 0 || args[len] != 0)
933 goto BadFormat;
934
935 redir = redir_find( client->global, host_port, host_proto );
936 if ( redir != NULL ) {
937 control_write( client, "KO: host port already active, use 'redir del' to remove first\r\n" );
938 return -1;
939 }
940
David Turner7d9a2702009-04-14 14:43:24 -0700941 if (inet_strtoip("10.0.2.15", &guest_ip) < 0) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800942 control_write( client, "KO: unexpected internal failure when resolving 10.0.2.15\r\n" );
943 return -1;
944 }
945
946 D(("pattern hport=%d gport=%d proto=%d\n", host_port, guest_port, host_proto ));
947 if ( control_global_add_redir( client->global, host_port, host_proto,
948 guest_ip, guest_port ) < 0 )
949 {
950 control_write( client, "KO: not enough memory to allocate redirection\r\n" );
951 return -1;
952 }
953
954 if (slirp_redir(host_proto, host_port, guest_ip, guest_port) < 0) {
955 control_write( client, "KO: can't setup redirection, port probably used by another program on host\r\n" );
956 control_global_del_redir( client->global, host_port, host_proto );
957 return -1;
958 }
959
960 return 0;
961
962BadFormat:
963 control_write( client, "KO: bad redirection format, try (tcp|udp):hostport:guestport\r\n", -1 );
964 return -1;
965}
966
967
968static int
969do_redir_del( ControlClient client, char* args )
970{
971 int len, proto, port;
972 Redir redir;
973
974 if ( !args )
975 goto BadFormat;
976 len = redir_parse_proto_port( args, &port, &proto );
977 if ( len == 0 || args[len] != 0 )
978 goto BadFormat;
979
980 redir = redir_find( client->global, port, proto );
981 if (redir == NULL) {
982 control_write( client, "KO: can't remove unknown redirection (%s:%d)\r\n",
983 proto ? "udp" : "tcp", port );
984 return -1;
985 }
986
987 slirp_unredir( redir->host_udp, redir->host_port );
988 control_global_del_redir( client->global, port, proto );\
989
990 return 0;
991
992BadFormat:
993 control_write( client, "KO: bad redirection format, try (tcp|udp):hostport\r\n" );
994 return -1;
995}
996
997static const CommandDefRec redir_commands[] =
998{
999 { "list", "list current redirections",
1000 "list current port redirections. use 'redir add' and 'redir del' to add and remove them\r\n", NULL,
1001 do_redir_list, NULL },
1002
1003 { "add", "add new redirection",
1004 "add a new port redirection, arguments must be:\r\n\r\n"
1005 " redir add <protocol>:<host-port>:<guest-port>\r\n\r\n"
1006 "where: <protocol> is either 'tcp' or 'udp'\r\n"
1007 " <host-port> a number indicating which port on the host to open\r\n"
1008 " <guest-port> a number indicating which port to route to on the device\r\n"
1009 "\r\nas an example, 'redir tcp:5000:6000' will allow any packets sent to\r\n"
1010 "the host's TCP port 5000 to be routed to TCP port 6000 of the emulated device\r\n", NULL,
1011 do_redir_add, NULL },
1012
1013 { "del", "remove existing redirection",
1014 "remove a port redirecion that was created with 'redir add', arguments must be:\r\n\r\n"
1015 " redir del <protocol>:<host-port>\r\n\r\n"
1016 "see the 'help redir add' for the meaning of <protocol> and <host-port>\r\n", NULL,
1017 do_redir_del, NULL },
1018
1019 { NULL, NULL, NULL, NULL, NULL, NULL }
1020};
1021
1022
1023
1024/********************************************************************************************/
1025/********************************************************************************************/
1026/***** ******/
Jaime Lopez1a000852010-07-21 18:03:58 -07001027/***** C D M A M O D E M ******/
1028/***** ******/
1029/********************************************************************************************/
1030/********************************************************************************************/
1031
1032static const struct {
1033 const char * name;
1034 const char * display;
1035 ACdmaSubscriptionSource source;
1036} _cdma_subscription_sources[] = {
1037 { "nv", "Read subscription from non-volatile RAM", A_SUBSCRIPTION_NVRAM },
1038 { "ruim", "Read subscription from RUIM", A_SUBSCRIPTION_RUIM },
1039};
1040
1041static void
1042dump_subscription_sources( ControlClient client )
1043{
1044 int i;
1045 for (i = 0;
1046 i < sizeof(_cdma_subscription_sources) / sizeof(_cdma_subscription_sources[0]);
1047 i++) {
1048 control_write( client, " %s: %s\r\n",
1049 _cdma_subscription_sources[i].name,
1050 _cdma_subscription_sources[i].display );
1051 }
1052}
1053
1054static void
1055describe_subscription_source( ControlClient client )
1056{
1057 control_write( client,
1058 "'cdma ssource <ssource>' allows you to specify where to read the subscription from\r\n" );
1059 dump_subscription_sources( client );
1060}
1061
1062static int
1063do_cdma_ssource( ControlClient client, char* args )
1064{
1065 int nn;
1066 if (!args) {
1067 control_write( client, "KO: missing argument, try 'cdma ssource <source>'\r\n" );
1068 return -1;
1069 }
1070
1071 for (nn = 0; ; nn++) {
1072 const char* name = _cdma_subscription_sources[nn].name;
1073 ACdmaSubscriptionSource ssource = _cdma_subscription_sources[nn].source;
1074
1075 if (!name)
1076 break;
1077
1078 if (!strcasecmp( args, name )) {
1079 amodem_set_cdma_subscription_source( android_modem, ssource );
1080 return 0;
1081 }
1082 }
1083 control_write( client, "KO: Don't know source %s\r\n", args );
1084 return -1;
1085}
1086
1087static int
1088do_cdma_prl_version( ControlClient client, char * args )
1089{
1090 int version = 0;
1091 char *endptr;
1092
1093 if (!args) {
1094 control_write( client, "KO: missing argument, try 'cdma prl_version <version>'\r\n");
1095 return -1;
1096 }
1097
1098 version = strtol(args, &endptr, 0);
1099 if (endptr != args) {
1100 amodem_set_cdma_prl_version( android_modem, version );
1101 }
David 'Digit' Turner80bc5c82010-10-20 19:04:51 +02001102 return 0;
Jaime Lopez1a000852010-07-21 18:03:58 -07001103}
1104/********************************************************************************************/
1105/********************************************************************************************/
1106/***** ******/
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001107/***** G S M M O D E M ******/
1108/***** ******/
1109/********************************************************************************************/
1110/********************************************************************************************/
1111
1112static const struct {
1113 const char* name;
1114 const char* display;
1115 ARegistrationState state;
1116} _gsm_states[] = {
1117 { "unregistered", "no network available", A_REGISTRATION_UNREGISTERED },
1118 { "home", "on local network, non-roaming", A_REGISTRATION_HOME },
1119 { "roaming", "on roaming network", A_REGISTRATION_ROAMING },
1120 { "searching", "searching networks", A_REGISTRATION_SEARCHING },
1121 { "denied", "emergency calls only", A_REGISTRATION_DENIED },
1122 { "off", "same as 'unregistered'", A_REGISTRATION_UNREGISTERED },
1123 { "on", "same as 'home'", A_REGISTRATION_HOME },
1124 { NULL, NULL, A_REGISTRATION_UNREGISTERED }
1125};
1126
1127static const char*
1128gsm_state_to_string( ARegistrationState state )
1129{
1130 int nn;
1131 for (nn = 0; _gsm_states[nn].name != NULL; nn++) {
1132 if (state == _gsm_states[nn].state)
1133 return _gsm_states[nn].name;
1134 }
1135 return "<unknown>";
1136}
1137
1138static int
1139do_gsm_status( ControlClient client, char* args )
1140{
1141 if (args) {
1142 control_write( client, "KO: no argument required\r\n" );
1143 return -1;
1144 }
1145 if (!android_modem) {
1146 control_write( client, "KO: modem emulation not running\r\n" );
1147 return -1;
1148 }
1149 control_write( client, "gsm voice state: %s\r\n",
1150 gsm_state_to_string(
1151 amodem_get_voice_registration(android_modem) ) );
1152 control_write( client, "gsm data state: %s\r\n",
1153 gsm_state_to_string(
1154 amodem_get_data_registration(android_modem) ) );
1155 return 0;
1156}
1157
1158
1159static void
1160help_gsm_data( ControlClient client )
1161{
1162 int nn;
1163 control_write( client,
1164 "the 'gsm data <state>' allows you to change the state of your GPRS connection\r\n"
1165 "valid values for <state> are the following:\r\n\r\n" );
1166 for (nn = 0; ; nn++) {
1167 const char* name = _gsm_states[nn].name;
1168 const char* display = _gsm_states[nn].display;
1169
1170 if (!name)
1171 break;
1172
1173 control_write( client, " %-15s %s\r\n", name, display );
1174 }
1175 control_write( client, "\r\n" );
1176}
1177
1178
1179static int
1180do_gsm_data( ControlClient client, char* args )
1181{
1182 int nn;
1183
1184 if (!args) {
1185 control_write( client, "KO: missing argument, try 'gsm data <state>'\r\n" );
1186 return -1;
1187 }
1188
1189 for (nn = 0; ; nn++) {
1190 const char* name = _gsm_states[nn].name;
1191 ARegistrationState state = _gsm_states[nn].state;
1192
1193 if (!name)
1194 break;
1195
1196 if ( !strcmp( args, name ) ) {
1197 if (!android_modem) {
1198 control_write( client, "KO: modem emulation not running\r\n" );
1199 return -1;
1200 }
1201 amodem_set_data_registration( android_modem, state );
1202 qemu_net_disable = (state != A_REGISTRATION_HOME &&
1203 state != A_REGISTRATION_ROAMING );
1204 return 0;
1205 }
1206 }
1207 control_write( client, "KO: bad GSM data state name, try 'help gsm data' for list of valid values\r\n" );
1208 return -1;
1209}
1210
1211static void
1212help_gsm_voice( ControlClient client )
1213{
1214 int nn;
1215 control_write( client,
1216 "the 'gsm voice <state>' allows you to change the state of your GPRS connection\r\n"
1217 "valid values for <state> are the following:\r\n\r\n" );
1218 for (nn = 0; ; nn++) {
1219 const char* name = _gsm_states[nn].name;
1220 const char* display = _gsm_states[nn].display;
1221
1222 if (!name)
1223 break;
1224
1225 control_write( client, " %-15s %s\r\n", name, display );
1226 }
1227 control_write( client, "\r\n" );
1228}
1229
1230
1231static int
1232do_gsm_voice( ControlClient client, char* args )
1233{
1234 int nn;
1235
1236 if (!args) {
1237 control_write( client, "KO: missing argument, try 'gsm voice <state>'\r\n" );
1238 return -1;
1239 }
1240
1241 for (nn = 0; ; nn++) {
1242 const char* name = _gsm_states[nn].name;
1243 ARegistrationState state = _gsm_states[nn].state;
1244
1245 if (!name)
1246 break;
1247
1248 if ( !strcmp( args, name ) ) {
1249 if (!android_modem) {
1250 control_write( client, "KO: modem emulation not running\r\n" );
1251 return -1;
1252 }
1253 amodem_set_voice_registration( android_modem, state );
1254 return 0;
1255 }
1256 }
1257 control_write( client, "KO: bad GSM data state name, try 'help gsm voice' for list of valid values\r\n" );
1258 return -1;
1259}
1260
1261
1262static int
1263gsm_check_number( char* args )
1264{
1265 int nn;
1266
1267 for (nn = 0; args[nn] != 0; nn++) {
1268 int c = args[nn];
1269 if ( !isdigit(c) && c != '+' && c != '#' ) {
1270 return -1;
1271 }
1272 }
1273 if (nn == 0)
1274 return -1;
1275
1276 return 0;
1277}
1278
1279static int
1280do_gsm_call( ControlClient client, char* args )
1281{
1282 /* check that we have a phone number made of digits */
1283 if (!args) {
1284 control_write( client, "KO: missing argument, try 'gsm call <phonenumber>'\r\n" );
1285 return -1;
1286 }
1287
1288 if (gsm_check_number(args)) {
1289 control_write( client, "KO: bad phone number format, use digits, # and + only\r\n" );
1290 return -1;
1291 }
1292
1293 if (!android_modem) {
1294 control_write( client, "KO: modem emulation not running\r\n" );
1295 return -1;
1296 }
1297 amodem_add_inbound_call( android_modem, args );
1298 return 0;
1299}
1300
1301static int
1302do_gsm_cancel( ControlClient client, char* args )
1303{
1304 if (!args) {
1305 control_write( client, "KO: missing argument, try 'gsm call <phonenumber>'\r\n" );
1306 return -1;
1307 }
1308 if (gsm_check_number(args)) {
1309 control_write( client, "KO: bad phone number format, use digits, # and + only\r\n" );
1310 return -1;
1311 }
1312 if (!android_modem) {
1313 control_write( client, "KO: modem emulation not running\r\n" );
1314 return -1;
1315 }
1316 if ( amodem_disconnect_call( android_modem, args ) < 0 ) {
1317 control_write( client, "KO: could not cancel this number\r\n" );
1318 return -1;
1319 }
1320 return 0;
1321}
1322
1323
1324static const char*
1325call_state_to_string( ACallState state )
1326{
1327 switch (state) {
1328 case A_CALL_ACTIVE: return "active";
1329 case A_CALL_HELD: return "held";
1330 case A_CALL_ALERTING: return "ringing";
1331 case A_CALL_WAITING: return "waiting";
1332 case A_CALL_INCOMING: return "incoming";
1333 default: return "unknown";
1334 }
1335}
1336
1337static int
1338do_gsm_list( ControlClient client, char* args )
1339{
1340 /* check that we have a phone number made of digits */
1341 int count = amodem_get_call_count( android_modem );
1342 int nn;
1343 for (nn = 0; nn < count; nn++) {
1344 ACall call = amodem_get_call( android_modem, nn );
1345 const char* dir;
1346
1347 if (call == NULL)
1348 continue;
1349
1350 if (call->dir == A_CALL_OUTBOUND)
1351 dir = "outbound to ";
1352 else
1353 dir = "inbound from";
1354
1355 control_write( client, "%s %-10s : %s\r\n", dir,
1356 call->number, call_state_to_string(call->state) );
1357 }
1358 return 0;
1359}
1360
1361static int
1362do_gsm_busy( ControlClient client, char* args )
1363{
1364 ACall call;
1365
1366 if (!args) {
1367 control_write( client, "KO: missing argument, try 'gsm busy <phonenumber>'\r\n" );
1368 return -1;
1369 }
1370 call = amodem_find_call_by_number( android_modem, args );
1371 if (call == NULL || call->dir != A_CALL_OUTBOUND) {
1372 control_write( client, "KO: no current outbound call to number '%s' (call %p)\r\n", args, call );
1373 return -1;
1374 }
1375 if ( amodem_disconnect_call( android_modem, args ) < 0 ) {
1376 control_write( client, "KO: could not cancel this number\r\n" );
1377 return -1;
1378 }
1379 return 0;
1380}
1381
1382static int
1383do_gsm_hold( ControlClient client, char* args )
1384{
1385 ACall call;
1386
1387 if (!args) {
1388 control_write( client, "KO: missing argument, try 'gsm out hold <phonenumber>'\r\n" );
1389 return -1;
1390 }
1391 call = amodem_find_call_by_number( android_modem, args );
1392 if (call == NULL) {
1393 control_write( client, "KO: no current call to/from number '%s'\r\n", args );
1394 return -1;
1395 }
1396 if ( amodem_update_call( android_modem, args, A_CALL_HELD ) < 0 ) {
1397 control_write( client, "KO: could put this call on hold\r\n" );
1398 return -1;
1399 }
1400 return 0;
1401}
1402
1403
1404static int
1405do_gsm_accept( ControlClient client, char* args )
1406{
1407 ACall call;
1408
1409 if (!args) {
1410 control_write( client, "KO: missing argument, try 'gsm accept <phonenumber>'\r\n" );
1411 return -1;
1412 }
1413 call = amodem_find_call_by_number( android_modem, args );
1414 if (call == NULL) {
1415 control_write( client, "KO: no current call to/from number '%s'\r\n", args );
1416 return -1;
1417 }
1418 if ( amodem_update_call( android_modem, args, A_CALL_ACTIVE ) < 0 ) {
1419 control_write( client, "KO: could not activate this call\r\n" );
1420 return -1;
1421 }
1422 return 0;
1423}
1424
Tim Baverstock4c6b10a2010-12-15 17:31:13 +00001425static int
1426do_gsm_signal( ControlClient client, char* args )
1427{
1428 enum { SIGNAL_RSSI = 0, SIGNAL_BER, NUM_SIGNAL_PARAMS };
1429 char* p = args;
1430 int top_param = -1;
1431 int params[ NUM_SIGNAL_PARAMS ];
1432
1433 static int last_ber = 99;
1434
1435 if (!p)
1436 p = "";
1437
1438 /* tokenize */
1439 while (*p) {
1440 char* end;
1441 int val = strtol( p, &end, 10 );
1442
1443 if (end == p) {
1444 control_write( client, "KO: argument '%s' is not a number\n", p );
1445 return -1;
1446 }
1447
1448 params[++top_param] = val;
1449 if (top_param + 1 == NUM_SIGNAL_PARAMS)
1450 break;
1451
1452 p = end;
1453 while (*p && (p[0] == ' ' || p[0] == '\t'))
1454 p += 1;
1455 }
1456
1457 /* sanity check */
1458 if (top_param < SIGNAL_RSSI) {
1459 control_write( client, "KO: not enough arguments: see 'help gsm signal' for details\r\n" );
1460 return -1;
1461 }
1462
1463 int rssi = params[SIGNAL_RSSI];
1464 if ((rssi < 0 || rssi > 31) && rssi != 99) {
1465 control_write( client, "KO: invalid RSSI - must be 0..31 or 99\r\n");
1466 return -1;
1467 }
1468
1469 /* check ber is 0..7 or 99 */
1470 if (top_param >= SIGNAL_BER) {
1471 int ber = params[SIGNAL_BER];
1472 if ((ber < 0 || ber > 7) && ber != 99) {
1473 control_write( client, "KO: invalid BER - must be 0..7 or 99\r\n");
1474 return -1;
1475 }
1476 last_ber = ber;
1477 }
1478
1479 amodem_set_signal_strength( android_modem, rssi, last_ber );
1480
1481 return 0;
1482 }
1483
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001484
1485#if 0
1486static const CommandDefRec gsm_in_commands[] =
1487{
1488 { "new", "create a new 'waiting' inbound call",
1489 "'gsm in create <phonenumber>' creates a new inbound phone call, placed in\r\n"
1490 "the 'waiting' state by default, until the system answers/holds/closes it\r\n", NULL
1491 do_gsm_in_create, NULL },
1492
1493 { "hold", "change the state of an oubtound call to 'held'",
1494 "change the state of an outbound call to 'held'. this is only possible\r\n"
1495 "if the call in the 'waiting' or 'active' state\r\n", NULL,
1496 do_gsm_out_hold, NULL },
1497
1498 { "accept", "change the state of an outbound call to 'active'",
1499 "change the state of an outbound call to 'active'. this is only possible\r\n"
1500 "if the call is in the 'waiting' or 'held' state\r\n", NULL,
1501 do_gsm_out_accept, NULL },
1502
1503 { NULL, NULL, NULL, NULL, NULL, NULL }
1504};
1505#endif
1506
1507
Jaime Lopez1a000852010-07-21 18:03:58 -07001508static const CommandDefRec cdma_commands[] =
1509{
1510 { "ssource", "Set the current CDMA subscription source",
1511 NULL, describe_subscription_source,
1512 do_cdma_ssource, NULL },
David 'Digit' Turner80bc5c82010-10-20 19:04:51 +02001513 { "prl_version", "Dump the current PRL version",
1514 NULL, NULL,
1515 do_cdma_prl_version, NULL },
Jaime Lopez1a000852010-07-21 18:03:58 -07001516};
1517
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001518static const CommandDefRec gsm_commands[] =
1519{
1520 { "list", "list current phone calls",
1521 "'gsm list' lists all inbound and outbound calls and their state\r\n", NULL,
1522 do_gsm_list, NULL },
1523
1524 { "call", "create inbound phone call",
1525 "'gsm call <phonenumber>' allows you to simulate a new inbound call\r\n", NULL,
1526 do_gsm_call, NULL },
1527
1528 { "busy", "close waiting outbound call as busy",
1529 "'gsm busy <remoteNumber>' closes an outbound call, reporting\r\n"
1530 "the remote phone as busy. only possible if the call is 'waiting'.\r\n", NULL,
1531 do_gsm_busy, NULL },
1532
1533 { "hold", "change the state of an oubtound call to 'held'",
1534 "'gsm hold <remoteNumber>' change the state of a call to 'held'. this is only possible\r\n"
1535 "if the call in the 'waiting' or 'active' state\r\n", NULL,
1536 do_gsm_hold, NULL },
1537
1538 { "accept", "change the state of an outbound call to 'active'",
1539 "'gsm accept <remoteNumber>' change the state of a call to 'active'. this is only possible\r\n"
1540 "if the call is in the 'waiting' or 'held' state\r\n", NULL,
1541 do_gsm_accept, NULL },
1542
1543 { "cancel", "disconnect an inbound or outbound phone call",
1544 "'gsm cancel <phonenumber>' allows you to simulate the end of an inbound or outbound call\r\n", NULL,
1545 do_gsm_cancel, NULL },
1546
1547 { "data", "modify data connection state", NULL, help_gsm_data,
1548 do_gsm_data, NULL },
1549
1550 { "voice", "modify voice connection state", NULL, help_gsm_voice,
1551 do_gsm_voice, NULL },
1552
1553 { "status", "display GSM status",
1554 "'gsm status' displays the current state of the GSM emulation\r\n", NULL,
1555 do_gsm_status, NULL },
1556
Tim Baverstock4c6b10a2010-12-15 17:31:13 +00001557 { "signal", "set sets the rssi and ber",
1558 "'gsm signal <rssi> [<ber>]' changes the reported strength and error rate on next (15s) update.\r\n"
1559 "rssi range is 0..31 and 99 for unknown\r\n"
1560 "ber range is 0..7 percent and 99 for unknown\r\n",
1561 NULL, do_gsm_signal, NULL },
1562
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001563 { NULL, NULL, NULL, NULL, NULL, NULL }
1564};
1565
1566/********************************************************************************************/
1567/********************************************************************************************/
1568/***** ******/
1569/***** S M S C O M M A N D ******/
1570/***** ******/
1571/********************************************************************************************/
1572/********************************************************************************************/
1573
1574static int
1575do_sms_send( ControlClient client, char* args )
1576{
1577 char* p;
1578 int textlen;
1579 SmsAddressRec sender;
1580 SmsPDU* pdus;
1581 int nn;
1582
1583 /* check that we have a phone number made of digits */
1584 if (!args) {
1585 MissingArgument:
1586 control_write( client, "KO: missing argument, try 'sms send <phonenumber> <text message>'\r\n" );
1587 return -1;
1588 }
1589 p = strchr( args, ' ' );
1590 if (!p) {
1591 goto MissingArgument;
1592 }
1593
1594 if ( sms_address_from_str( &sender, args, p - args ) < 0 ) {
1595 control_write( client, "KO: bad phone number format, must be [+](0-9)*\r\n" );
1596 return -1;
1597 }
1598
1599
1600 /* un-secape message text into proper utf-8 (conversion happens in-site) */
1601 p += 1;
1602 textlen = strlen(p);
1603 textlen = sms_utf8_from_message_str( p, textlen, (unsigned char*)p, textlen );
1604 if (textlen < 0) {
1605 control_write( client, "message must be utf8 and can use the following escapes:\r\n"
1606 " \\n for a newline\r\n"
1607 " \\xNN where NN are two hexadecimal numbers\r\n"
1608 " \\uNNNN where NNNN are four hexadecimal numbers\r\n"
1609 " \\\\ to send a '\\' character\r\n\r\n"
1610 " anything else is an error\r\n"
1611 "KO: badly formatted text\r\n" );
1612 return -1;
1613 }
1614
1615 if (!android_modem) {
1616 control_write( client, "KO: modem emulation not running\r\n" );
1617 return -1;
1618 }
1619
1620 /* create a list of SMS PDUs, then send them */
1621 pdus = smspdu_create_deliver_utf8( (cbytes_t)p, textlen, &sender, NULL );
1622 if (pdus == NULL) {
1623 control_write( client, "KO: internal error when creating SMS-DELIVER PDUs\n" );
1624 return -1;
1625 }
1626
1627 for (nn = 0; pdus[nn] != NULL; nn++)
1628 amodem_receive_sms( android_modem, pdus[nn] );
1629
1630 smspdu_free_list( pdus );
1631 return 0;
1632}
1633
1634static int
1635do_sms_sendpdu( ControlClient client, char* args )
1636{
1637 SmsPDU pdu;
1638
1639 /* check that we have a phone number made of digits */
1640 if (!args) {
1641 control_write( client, "KO: missing argument, try 'sms sendpdu <hexstring>'\r\n" );
1642 return -1;
1643 }
1644
1645 if (!android_modem) {
1646 control_write( client, "KO: modem emulation not running\r\n" );
1647 return -1;
1648 }
1649
1650 pdu = smspdu_create_from_hex( args, strlen(args) );
1651 if (pdu == NULL) {
1652 control_write( client, "KO: badly formatted <hexstring>\r\n" );
1653 return -1;
1654 }
1655
1656 amodem_receive_sms( android_modem, pdu );
1657 smspdu_free( pdu );
1658 return 0;
1659}
1660
1661static const CommandDefRec sms_commands[] =
1662{
1663 { "send", "send inbound SMS text message",
1664 "'sms send <phonenumber> <message>' allows you to simulate a new inbound sms message\r\n", NULL,
1665 do_sms_send, NULL },
1666
1667 { "pdu", "send inbound SMS PDU",
1668 "'sms pdu <hexstring>' allows you to simulate a new inbound sms PDU\r\n"
1669 "(used internally when one emulator sends SMS messages to another instance).\r\n"
1670 "you probably don't want to play with this at all\r\n", NULL,
1671 do_sms_sendpdu, NULL },
1672
1673 { NULL, NULL, NULL, NULL, NULL, NULL }
1674};
1675
1676static void
1677do_control_write(void* data, const char* string)
1678{
1679 control_write((ControlClient)data, string);
1680}
1681
1682static int
1683do_power_display( ControlClient client, char* args )
1684{
1685 goldfish_battery_display(do_control_write, client);
1686 return 0;
1687}
1688
1689static int
1690do_ac_state( ControlClient client, char* args )
1691{
1692 if (args) {
1693 if (strcasecmp(args, "on") == 0) {
1694 goldfish_battery_set_prop(1, POWER_SUPPLY_PROP_ONLINE, 1);
1695 return 0;
1696 }
1697 if (strcasecmp(args, "off") == 0) {
1698 goldfish_battery_set_prop(1, POWER_SUPPLY_PROP_ONLINE, 0);
1699 return 0;
1700 }
1701 }
1702
1703 control_write( client, "KO: Usage: \"ac on\" or \"ac off\"\n" );
1704 return -1;
1705}
1706
1707static int
1708do_battery_status( ControlClient client, char* args )
1709{
1710 if (args) {
1711 if (strcasecmp(args, "unknown") == 0) {
1712 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_UNKNOWN);
1713 return 0;
1714 }
1715 if (strcasecmp(args, "charging") == 0) {
1716 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_CHARGING);
1717 return 0;
1718 }
1719 if (strcasecmp(args, "discharging") == 0) {
1720 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_DISCHARGING);
1721 return 0;
1722 }
1723 if (strcasecmp(args, "not-charging") == 0) {
1724 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_NOT_CHARGING);
1725 return 0;
1726 }
1727 if (strcasecmp(args, "full") == 0) {
1728 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_FULL);
1729 return 0;
1730 }
1731 }
1732
1733 control_write( client, "KO: Usage: \"status unknown|charging|discharging|not-charging|full\"\n" );
1734 return -1;
1735}
1736
1737static int
1738do_battery_present( ControlClient client, char* args )
1739{
1740 if (args) {
1741 if (strcasecmp(args, "true") == 0) {
1742 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_PRESENT, 1);
1743 return 0;
1744 }
1745 if (strcasecmp(args, "false") == 0) {
1746 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_PRESENT, 0);
1747 return 0;
1748 }
1749 }
1750
1751 control_write( client, "KO: Usage: \"present true\" or \"present false\"\n" );
1752 return -1;
1753}
1754
1755static int
1756do_battery_health( ControlClient client, char* args )
1757{
1758 if (args) {
1759 if (strcasecmp(args, "unknown") == 0) {
1760 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_UNKNOWN);
1761 return 0;
1762 }
1763 if (strcasecmp(args, "good") == 0) {
1764 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_GOOD);
1765 return 0;
1766 }
1767 if (strcasecmp(args, "overheat") == 0) {
1768 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_OVERHEAT);
1769 return 0;
1770 }
1771 if (strcasecmp(args, "dead") == 0) {
1772 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_DEAD);
1773 return 0;
1774 }
1775 if (strcasecmp(args, "overvoltage") == 0) {
1776 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_OVERVOLTAGE);
1777 return 0;
1778 }
1779 if (strcasecmp(args, "failure") == 0) {
1780 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_UNSPEC_FAILURE);
1781 return 0;
1782 }
1783 }
1784
1785 control_write( client, "KO: Usage: \"health unknown|good|overheat|dead|overvoltage|failure\"\n" );
1786 return -1;
1787}
1788
1789static int
1790do_battery_capacity( ControlClient client, char* args )
1791{
1792 if (args) {
1793 int capacity;
1794
1795 if (sscanf(args, "%d", &capacity) == 1 && capacity >= 0 && capacity <= 100) {
1796 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_CAPACITY, capacity);
1797 return 0;
1798 }
1799 }
1800
1801 control_write( client, "KO: Usage: \"capacity <percentage>\"\n" );
1802 return -1;
1803}
1804
1805
1806static const CommandDefRec power_commands[] =
1807{
1808 { "display", "display battery and charger state",
1809 "display battery and charger state\r\n", NULL,
1810 do_power_display, NULL },
1811
1812 { "ac", "set AC charging state",
1813 "'ac on|off' allows you to set the AC charging state to on or off\r\n", NULL,
1814 do_ac_state, NULL },
1815
1816 { "status", "set battery status",
1817 "'status unknown|charging|discharging|not-charging|full' allows you to set battery status\r\n", NULL,
1818 do_battery_status, NULL },
1819
1820 { "present", "set battery present state",
1821 "'present true|false' allows you to set battery present state to true or false\r\n", NULL,
1822 do_battery_present, NULL },
1823
1824 { "health", "set battery health state",
1825 "'health unknown|good|overheat|dead|overvoltage|failure' allows you to set battery health state\r\n", NULL,
1826 do_battery_health, NULL },
1827
1828 { "capacity", "set battery capacity state",
1829 "'capacity <percentage>' allows you to set battery capacity to a value 0 - 100\r\n", NULL,
1830 do_battery_capacity, NULL },
1831
1832 { NULL, NULL, NULL, NULL, NULL, NULL }
1833};
1834
1835/********************************************************************************************/
1836/********************************************************************************************/
1837/***** ******/
1838/***** E V E N T C O M M A N D S ******/
1839/***** ******/
1840/********************************************************************************************/
1841/********************************************************************************************/
1842
1843
1844static int
1845do_event_send( ControlClient client, char* args )
1846{
1847 char* p;
1848
1849 if (!args) {
1850 control_write( client, "KO: Usage: event send <type>:<code>:<value> ...\r\n" );
1851 return -1;
1852 }
1853
1854 p = args;
1855 while (*p) {
1856 char* q;
David 'Digit' Turner88935f72011-05-09 10:24:18 +02001857 char temp[128];
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001858 int type, code, value, ret;
1859
David 'Digit' Turner88935f72011-05-09 10:24:18 +02001860 p += strspn( p, " \t" ); /* skip spaces */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001861 if (*p == 0)
1862 break;
1863
1864 q = p + strcspn( p, " \t" );
1865
1866 if (q == p)
1867 break;
1868
David 'Digit' Turner88935f72011-05-09 10:24:18 +02001869 snprintf(temp, sizeof temp, "%.*s", q-p, p);
1870 ret = android_event_from_str( temp, &type, &code, &value );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001871 if (ret < 0) {
1872 if (ret == -1) {
1873 control_write( client,
1874 "KO: invalid event type in '%.*s', try 'event list types' for valid values\r\n",
1875 q-p, p );
1876 } else if (ret == -2) {
1877 control_write( client,
1878 "KO: invalid event code in '%.*s', try 'event list codes <type>' for valid values\r\n",
1879 q-p, p );
1880 } else {
1881 control_write( client,
1882 "KO: invalid event value in '%.*s', must be an integer\r\n",
1883 q-p, p);
1884 }
1885 return -1;
1886 }
1887
David 'Digit' Turner34f29742010-05-25 18:16:10 -07001888 user_event_generic( type, code, value );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001889 p = q;
1890 }
1891 return 0;
1892}
1893
1894static int
1895do_event_types( ControlClient client, char* args )
1896{
1897 int count = android_event_get_type_count();
1898 int nn;
1899
1900 control_write( client, "event <type> can be an integer or one of the following aliases\r\n" );
1901 for (nn = 0; nn < count; nn++) {
1902 char tmp[16];
1903 char* p = tmp;
1904 char* end = p + sizeof(tmp);
1905 int count2 = android_event_get_code_count( nn );;
1906
1907 p = android_event_bufprint_type_str( p, end, nn );
1908
1909 control_write( client, " %-8s", tmp );
1910 if (count2 > 0)
1911 control_write( client, " (%d code aliases)", count2 );
1912
1913 control_write( client, "\r\n" );
1914 }
1915 return 0;
1916}
1917
1918static int
1919do_event_codes( ControlClient client, char* args )
1920{
1921 int count;
1922 int nn, type, dummy;
1923
1924 if (!args) {
1925 control_write( client, "KO: argument missing, try 'event codes <type>'\r\n" );
1926 return -1;
1927 }
1928
1929 if ( android_event_from_str( args, &type, &dummy, &dummy ) < 0 ) {
1930 control_write( client, "KO: bad argument, see 'event types' for valid values\r\n" );
1931 return -1;
1932 }
1933
1934 count = android_event_get_code_count( type );
1935 if (count == 0) {
1936 control_write( client, "no code aliases defined for this type\r\n" );
1937 } else {
1938 control_write( client, "type '%s' accepts the following <code> aliases:\r\n",
1939 args );
1940 for (nn = 0; nn < count; nn++) {
1941 char temp[20], *p = temp, *end = p + sizeof(temp);
1942 android_event_bufprint_code_str( p, end, type, nn );
1943 control_write( client, " %-12s\r\n", temp );
1944 }
1945 }
1946
1947 return 0;
1948}
1949
1950static __inline__ int
1951utf8_next( unsigned char* *pp, unsigned char* end )
1952{
1953 unsigned char* p = *pp;
1954 int result = -1;
1955
1956 if (p < end) {
1957 int c= *p++;
1958 if (c >= 128) {
1959 if ((c & 0xe0) == 0xc0)
1960 c &= 0x1f;
1961 else if ((c & 0xf0) == 0xe0)
1962 c &= 0x0f;
1963 else
1964 c &= 0x07;
1965
1966 while (p < end && (p[0] & 0xc0) == 0x80) {
1967 c = (c << 6) | (p[0] & 0x3f);
1968 }
1969 }
1970 result = c;
1971 *pp = p;
1972 }
1973 return result;
1974}
1975
1976static int
1977do_event_text( ControlClient client, char* args )
1978{
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001979 AKeycodeBuffer keycodes;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001980 unsigned char* p = (unsigned char*) args;
1981 unsigned char* end = p + strlen(args);
1982 int textlen;
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001983 const AKeyCharmap* charmap;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001984
1985 if (!args) {
1986 control_write( client, "KO: argument missing, try 'event text <message>'\r\n" );
1987 return -1;
1988 }
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001989
David 'Digit' Turner0158ea32011-01-19 05:21:31 +01001990 /* Get active charmap. */
1991 charmap = android_get_charmap();
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001992 if (charmap == NULL) {
1993 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 -08001994 return -1;
1995 }
1996
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001997 keycodes.keycode_count = 0;
1998
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001999 /* un-secape message text into proper utf-8 (conversion happens in-site) */
2000 textlen = strlen((char*)p);
2001 textlen = sms_utf8_from_message_str( args, textlen, (unsigned char*)p, textlen );
2002 if (textlen < 0) {
2003 control_write( client, "message must be utf8 and can use the following escapes:\r\n"
2004 " \\n for a newline\r\n"
2005 " \\xNN where NN are two hexadecimal numbers\r\n"
2006 " \\uNNNN where NNNN are four hexadecimal numbers\r\n"
2007 " \\\\ to send a '\\' character\r\n\r\n"
2008 " anything else is an error\r\n"
2009 "KO: badly formatted text\r\n" );
2010 return -1;
2011 }
2012
2013 end = p + textlen;
2014 while (p < end) {
2015 int c = utf8_next( &p, end );
2016 if (c <= 0)
2017 break;
2018
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07002019 android_charmap_reverse_map_unicode( NULL, (unsigned)c, 1, &keycodes );
2020 android_charmap_reverse_map_unicode( NULL, (unsigned)c, 0, &keycodes );
2021 android_keycodes_flush( &keycodes );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002022 }
2023
2024 return 0;
2025}
2026
2027static const CommandDefRec event_commands[] =
2028{
2029 { "send", "send a series of events to the kernel",
2030 "'event send <type>:<code>:<value> ...' allows your to send one or more hardware events\r\n"
2031 "to the Android kernel. you can use text names or integers for <type> and <code>\r\n", NULL,
2032 do_event_send, NULL },
2033
2034 { "types", "list all <type> aliases",
2035 "'event types' list all <type> string aliases supported by the 'event' subcommands\r\n",
2036 NULL, do_event_types, NULL },
2037
2038 { "codes", "list all <code> aliases for a given <type>",
2039 "'event codes <type>' lists all <code> string aliases for a given event <type>\r\n",
2040 NULL, do_event_codes, NULL },
2041
2042 { "text", "simulate keystrokes from a given text",
2043 "'event text <message>' allows you to simulate keypresses to generate a given text\r\n"
2044 "message. <message> must be an utf-8 string. Unicode points will be reverse-mapped\r\n"
2045 "according to the current device keyboard. unsupported characters will be discarded\r\n"
2046 "silently\r\n", NULL, do_event_text, NULL },
2047
2048 { NULL, NULL, NULL, NULL, NULL, NULL }
2049};
2050
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002051
2052/********************************************************************************************/
2053/********************************************************************************************/
2054/***** ******/
2055/***** S N A P S H O T C O M M A N D S ******/
2056/***** ******/
2057/********************************************************************************************/
2058/********************************************************************************************/
2059
2060static int
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002061control_write_out_cb(void* opaque, const char* str, int strsize)
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002062{
2063 ControlClient client = opaque;
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002064 control_control_write(client, str, strsize);
2065 return strsize;
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002066}
2067
2068static int
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002069control_write_err_cb(void* opaque, const char* str, int strsize)
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002070{
2071 int ret = 0;
2072 ControlClient client = opaque;
2073 ret += control_write(client, "KO: ");
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002074 control_control_write(client, str, strsize);
2075 return ret + strsize;
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002076}
2077
2078static int
2079do_snapshot_list( ControlClient client, char* args )
2080{
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002081 int64_t ret;
2082 Monitor *out = monitor_fake_new(client, control_write_out_cb);
2083 Monitor *err = monitor_fake_new(client, control_write_err_cb);
2084 do_info_snapshots(out, err);
2085 ret = monitor_fake_get_bytes(err);
2086 monitor_fake_free(err);
2087 monitor_fake_free(out);
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002088
2089 return ret > 0;
2090}
2091
2092static int
2093do_snapshot_save( ControlClient client, char* args )
2094{
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002095 int64_t ret;
David 'Digit' Turner9fb360e2011-05-04 22:01:28 +02002096
2097 if (args == NULL) {
2098 control_write(client, "KO: argument missing, try 'avd snapshot save <name>'\r\n");
2099 return -1;
2100 }
2101
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002102 Monitor *err = monitor_fake_new(client, control_write_err_cb);
2103 do_savevm(err, args);
2104 ret = monitor_fake_get_bytes(err);
2105 monitor_fake_free(err);
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002106
2107 return ret > 0; // no output on error channel indicates success
2108}
2109
2110static int
2111do_snapshot_load( ControlClient client, char* args )
2112{
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002113 int64_t ret;
David 'Digit' Turner9fb360e2011-05-04 22:01:28 +02002114
2115 if (args == NULL) {
2116 control_write(client, "KO: argument missing, try 'avd snapshot load <name>'\r\n");
2117 return -1;
2118 }
2119
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002120 Monitor *err = monitor_fake_new(client, control_write_err_cb);
2121 do_loadvm(err, args);
2122 ret = monitor_fake_get_bytes(err);
2123 monitor_fake_free(err);
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002124
2125 return ret > 0;
2126}
2127
2128static int
2129do_snapshot_del( ControlClient client, char* args )
2130{
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002131 int64_t ret;
David 'Digit' Turner9fb360e2011-05-04 22:01:28 +02002132
2133 if (args == NULL) {
2134 control_write(client, "KO: argument missing, try 'avd snapshot del <name>'\r\n");
2135 return -1;
2136 }
2137
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002138 Monitor *err = monitor_fake_new(client, control_write_err_cb);
2139 do_delvm(err, args);
2140 ret = monitor_fake_get_bytes(err);
2141 monitor_fake_free(err);
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002142
2143 return ret > 0;
2144}
2145
2146static const CommandDefRec snapshot_commands[] =
2147{
2148 { "list", "list available state snapshots",
2149 "'avd snapshot list' will show a list of all state snapshots that can be loaded\r\n",
2150 NULL, do_snapshot_list, NULL },
2151
2152 { "save", "save state snapshot",
2153 "'avd snapshot save <name>' will save the current (run-time) state to a snapshot with the given name\r\n",
2154 NULL, do_snapshot_save, NULL },
2155
2156 { "load", "load state snapshot",
2157 "'avd snapshot load <name>' will load the state snapshot of the given name\r\n",
2158 NULL, do_snapshot_load, NULL },
2159
2160 { "del", "delete state snapshot",
2161 "'avd snapshot del <name>' will delete the state snapshot with the given name\r\n",
2162 NULL, do_snapshot_del, NULL },
2163
2164 { NULL, NULL, NULL, NULL, NULL, NULL }
2165};
2166
2167
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002168
2169/********************************************************************************************/
2170/********************************************************************************************/
2171/***** ******/
2172/***** V M C O M M A N D S ******/
2173/***** ******/
2174/********************************************************************************************/
2175/********************************************************************************************/
2176
2177static int
2178do_avd_stop( ControlClient client, char* args )
2179{
2180 if (!vm_running) {
2181 control_write( client, "KO: virtual device already stopped\r\n" );
2182 return -1;
2183 }
2184 vm_stop(EXCP_INTERRUPT);
2185 return 0;
2186}
2187
2188static int
2189do_avd_start( ControlClient client, char* args )
2190{
2191 if (vm_running) {
2192 control_write( client, "KO: virtual device already running\r\n" );
2193 return -1;
2194 }
2195 vm_start();
2196 return 0;
2197}
2198
2199static int
2200do_avd_status( ControlClient client, char* args )
2201{
2202 control_write( client, "virtual device is %s\r\n", vm_running ? "running" : "stopped" );
2203 return 0;
2204}
2205
2206static int
2207do_avd_name( ControlClient client, char* args )
2208{
David 'Digit' Turnerec6cedb2011-05-05 12:49:51 +02002209 control_write( client, "%s\r\n", android_hw->avd_name);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002210 return 0;
2211}
2212
2213static const CommandDefRec vm_commands[] =
2214{
2215 { "stop", "stop the virtual device",
2216 "'avd stop' stops the virtual device immediately, use 'avd start' to continue execution\r\n",
2217 NULL, do_avd_stop, NULL },
2218
2219 { "start", "start/restart the virtual device",
2220 "'avd start' will start or continue the virtual device, use 'avd stop' to stop it\r\n",
2221 NULL, do_avd_start, NULL },
2222
2223 { "status", "query virtual device status",
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002224 "'avd status' will indicate whether the virtual device is running or not\r\n",
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002225 NULL, do_avd_status, NULL },
2226
2227 { "name", "query virtual device name",
2228 "'avd name' will return the name of this virtual device\r\n",
2229 NULL, do_avd_name, NULL },
2230
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002231 { "snapshot", "state snapshot commands",
2232 "allows you to save and restore the virtual device state in snapshots\r\n",
2233 NULL, NULL, snapshot_commands },
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002234
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002235 { NULL, NULL, NULL, NULL, NULL, NULL }
2236};
2237
2238/********************************************************************************************/
2239/********************************************************************************************/
2240/***** ******/
2241/***** G E O C O M M A N D S ******/
2242/***** ******/
2243/********************************************************************************************/
2244/********************************************************************************************/
2245
2246static int
2247do_geo_nmea( ControlClient client, char* args )
2248{
2249 if (!args) {
2250 control_write( client, "KO: NMEA sentence missing, try 'help geo nmea'\r\n" );
2251 return -1;
2252 }
2253 if (!android_gps_cs) {
2254 control_write( client, "KO: no GPS emulation in this virtual device\r\n" );
2255 return -1;
2256 }
2257 android_gps_send_nmea( args );
2258 return 0;
2259}
2260
2261static int
2262do_geo_fix( ControlClient client, char* args )
2263{
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002264 // GEO_SAT2 provides bug backwards compatibility.
2265 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 -08002266 char* p = args;
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002267 int top_param = -1;
2268 double params[ NUM_GEO_PARAMS ];
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002269 int n_satellites = 1;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002270
2271 static int last_time = 0;
2272 static double last_altitude = 0.;
2273
2274 if (!p)
2275 p = "";
2276
2277 /* tokenize */
2278 while (*p) {
2279 char* end;
2280 double val = strtod( p, &end );
2281
2282 if (end == p) {
2283 control_write( client, "KO: argument '%s' is not a number\n", p );
2284 return -1;
2285 }
2286
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002287 params[++top_param] = val;
2288 if (top_param + 1 == NUM_GEO_PARAMS)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002289 break;
2290
2291 p = end;
2292 while (*p && (p[0] == ' ' || p[0] == '\t'))
2293 p += 1;
2294 }
2295
2296 /* sanity check */
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002297 if (top_param < GEO_LAT) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002298 control_write( client, "KO: not enough arguments: see 'help geo fix' for details\r\n" );
2299 return -1;
2300 }
2301
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002302 /* check number of satellites, must be integer between 1 and 12 */
2303 if (top_param >= GEO_SAT) {
2304 int sat_index = (top_param >= GEO_SAT2) ? GEO_SAT2 : GEO_SAT;
2305 n_satellites = (int) params[sat_index];
2306 if (n_satellites != params[sat_index]
2307 || n_satellites < 1 || n_satellites > 12) {
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002308 control_write( client, "KO: invalid number of satellites. Must be an integer between 1 and 12\r\n");
2309 return -1;
2310 }
2311 }
2312
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002313 /* generate an NMEA sentence for this fix */
2314 {
2315 STRALLOC_DEFINE(s);
2316 double val;
2317 int deg, min;
2318 char hemi;
2319
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002320 /* format overview:
2321 * time of fix 123519 12:35:19 UTC
2322 * latitude 4807.038 48 degrees, 07.038 minutes
2323 * north/south N or S
2324 * longitude 01131.000 11 degrees, 31. minutes
2325 * east/west E or W
2326 * fix quality 1 standard GPS fix
2327 * satellites 1 to 12 number of satellites being tracked
2328 * HDOP <dontcare> horizontal dilution
2329 * altitude 546. altitude above sea-level
2330 * altitude units M to indicate meters
2331 * diff <dontcare> height of sea-level above ellipsoid
2332 * diff units M to indicate meters (should be <dontcare>)
2333 * dgps age <dontcare> time in seconds since last DGPS fix
2334 * dgps sid <dontcare> DGPS station id
2335 */
2336
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002337 /* first, the time */
2338 stralloc_add_format( s, "$GPGGA,%06d", last_time );
2339 last_time ++;
2340
2341 /* then the latitude */
2342 hemi = 'N';
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002343 val = params[GEO_LAT];
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002344 if (val < 0) {
2345 hemi = 'S';
2346 val = -val;
2347 }
2348 deg = (int) val;
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002349 val = 60*(val - deg);
2350 min = (int) val;
David 'Digit' Turner631f2552010-10-27 02:46:53 +02002351 val = 10000*(val - min);
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002352 stralloc_add_format( s, ",%02d%02d.%04d,%c", deg, min, (int)val, hemi );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002353
2354 /* the longitude */
2355 hemi = 'E';
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002356 val = params[GEO_LONG];
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002357 if (val < 0) {
2358 hemi = 'W';
2359 val = -val;
2360 }
2361 deg = (int) val;
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002362 val = 60*(val - deg);
2363 min = (int) val;
David 'Digit' Turner631f2552010-10-27 02:46:53 +02002364 val = 10000*(val - min);
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002365 stralloc_add_format( s, ",%02d%02d.%04d,%c", deg, min, (int)val, hemi );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002366
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002367 /* bogus fix quality, satellite count and dilution */
2368 stralloc_add_format( s, ",1,%02d,", n_satellites );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002369
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002370 /* optional altitude + bogus diff */
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002371 if (top_param >= GEO_ALT) {
2372 stralloc_add_format( s, ",%.1g,M,0.,M", params[GEO_ALT] );
2373 last_altitude = params[GEO_ALT];
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002374 } else {
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002375 stralloc_add_str( s, ",,,," );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002376 }
2377 /* bogus rest and checksum */
2378 stralloc_add_str( s, ",,,*47" );
2379
2380 /* send it, then free */
2381 android_gps_send_nmea( stralloc_cstr(s) );
2382 stralloc_reset( s );
2383 }
2384 return 0;
2385}
2386
2387static const CommandDefRec geo_commands[] =
2388{
2389 { "nmea", "send an GPS NMEA sentence",
2390 "'geo nema <sentence>' sends a NMEA 0183 sentence to the emulated device, as\r\n"
2391 "if it came from an emulated GPS modem. <sentence> must begin with '$GP'. only\r\n"
2392 "'$GPGGA' and '$GPRCM' sentences are supported at the moment.\r\n",
2393 NULL, do_geo_nmea, NULL },
2394
2395 { "fix", "send a simple GPS fix",
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002396 "'geo fix <longitude> <latitude> [<altitude> [<satellites>]]'\r\n"
2397 " allows you to send a simple GPS fix to the emulated system.\r\n"
2398 " The parameters are:\r\n\r\n"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002399 " <longitude> longitude, in decimal degrees\r\n"
2400 " <latitude> latitude, in decimal degrees\r\n"
2401 " <altitude> optional altitude in meters\r\n"
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002402 " <satellites> number of satellites being tracked (1-12)\r\n"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002403 "\r\n",
2404 NULL, do_geo_fix, NULL },
2405
2406 { NULL, NULL, NULL, NULL, NULL, NULL }
2407};
2408
2409
2410/********************************************************************************************/
2411/********************************************************************************************/
2412/***** ******/
2413/***** M A I N C O M M A N D S ******/
2414/***** ******/
2415/********************************************************************************************/
2416/********************************************************************************************/
2417
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002418static int
2419do_window_scale( ControlClient client, char* args )
2420{
2421 double scale;
2422 int is_dpi = 0;
2423 char* end;
2424
2425 if (!args) {
2426 control_write( client, "KO: argument missing, try 'window scale <scale>'\r\n" );
2427 return -1;
2428 }
2429
2430 scale = strtol( args, &end, 10 );
2431 if (end > args && !memcmp( end, "dpi", 4 )) {
2432 is_dpi = 1;
2433 }
2434 else {
2435 scale = strtod( args, &end );
2436 if (end == args || end[0]) {
2437 control_write( client, "KO: argument <scale> must be a real number, or an integer followed by 'dpi'\r\n" );
2438 return -1;
2439 }
2440 }
2441
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -08002442 uicmd_set_window_scale( scale, is_dpi );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002443 return 0;
2444}
2445
2446static const CommandDefRec window_commands[] =
2447{
2448 { "scale", "change the window scale",
2449 "'window scale <scale>' allows you to change the scale of the emulator window at runtime\r\n"
2450 "<scale> must be either a real number between 0.1 and 3.0, or an integer followed by\r\n"
2451 "the 'dpi' prefix (as in '120dpi')\r\n",
2452 NULL, do_window_scale, NULL },
2453
2454 { NULL, NULL, NULL, NULL, NULL, NULL }
2455};
2456
2457/********************************************************************************************/
2458/********************************************************************************************/
2459/***** ******/
David 'Digit' Turnere92bc562010-09-07 06:21:25 -07002460/***** Q E M U C O M M A N D S ******/
2461/***** ******/
2462/********************************************************************************************/
2463/********************************************************************************************/
2464
2465static int
2466do_qemu_monitor( ControlClient client, char* args )
2467{
2468 char socketname[32];
2469 int fd;
2470 CharDriverState* cs;
2471
2472 if (args != NULL) {
2473 control_write( client, "KO: no argument for 'qemu monitor'\r\n" );
2474 return -1;
2475 }
2476 /* Detach the client socket, and re-attach it to a monitor */
2477 fd = control_client_detach(client);
2478 snprintf(socketname, sizeof socketname, "tcp:socket=%d", fd);
2479 cs = qemu_chr_open("monitor", socketname, NULL);
2480 if (cs == NULL) {
2481 control_client_reattach(client, fd);
2482 control_write( client, "KO: internal error: could not detach from console !\r\n" );
2483 return -1;
2484 }
2485 monitor_init(cs, MONITOR_USE_READLINE|MONITOR_QUIT_DOESNT_EXIT);
2486 control_client_destroy(client);
2487 return 0;
2488}
2489
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002490#ifdef CONFIG_STANDALONE_CORE
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002491/* UI settings, passed to the core via -ui-settings command line parameter. */
2492extern char* android_op_ui_settings;
2493
2494static int
2495do_attach_ui( ControlClient client, char* args )
2496{
2497 // Make sure that there are no UI already attached to this console.
2498 if (attached_ui_client != NULL) {
2499 control_write( client, "KO: Another UI is attached to this core!\r\n" );
2500 control_client_destroy(client);
2501 return -1;
2502 }
2503
Vladimir Chtchetkine85276802011-01-31 15:18:45 -08002504 if (!attachUiProxy_create(client->sock)) {
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002505 char reply_buf[4096];
Vladimir Chtchetkine85276802011-01-31 15:18:45 -08002506 attached_ui_client = client;
2507 // Reply "OK" with the saved -ui-settings property.
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002508 snprintf(reply_buf, sizeof(reply_buf), "OK: %s\r\n", android_op_ui_settings);
2509 control_write( client, reply_buf);
2510 } else {
Vladimir Chtchetkine85276802011-01-31 15:18:45 -08002511 control_write( client, "KO\r\n" );
2512 control_client_destroy(client);
2513 return -1;
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002514 }
2515
2516 return 0;
2517}
2518
Vladimir Chtchetkine85276802011-01-31 15:18:45 -08002519void
2520destroy_attach_ui_client(void)
2521{
2522 if (attached_ui_client != NULL) {
2523 control_client_destroy(attached_ui_client);
2524 }
2525}
2526
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002527static int
2528do_create_framebuffer_service( ControlClient client, char* args )
2529{
Vladimir Chtchetkine94a2fba2011-01-31 10:49:06 -08002530 ProxyFramebuffer* core_fb;
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002531 const char* protocol = "-raw"; // Default framebuffer exchange protocol.
David 'Digit' Turner7a5ee572011-02-02 15:58:45 +01002532 char reply_buf[64];
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002533
2534 // Protocol type is defined by the arguments passed with the stream switch
2535 // command.
2536 if (args != NULL && *args != '\0') {
2537 size_t token_len;
2538 const char* param_end = strchr(args, ' ');
2539 if (param_end == NULL) {
2540 param_end = args + strlen(args);
2541 }
2542 token_len = param_end - args;
2543 protocol = args;
2544
2545 // Make sure that this is one of the supported protocols.
2546 if (strncmp(protocol, "-raw", token_len) &&
2547 strncmp(protocol, "-shared", token_len)) {
2548 derror("Invalid framebuffer parameter %s\n", protocol);
2549 control_write( client, "KO: Invalid parameter\r\n" );
2550 control_client_destroy(client);
2551 return -1;
2552 }
2553 }
2554
David 'Digit' Turner7a5ee572011-02-02 15:58:45 +01002555 core_fb = proxyFb_create(client->sock, protocol);
2556 if (core_fb == NULL) {
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002557 control_write( client, "KO\r\n" );
2558 control_client_destroy(client);
2559 return -1;
2560 }
2561
David 'Digit' Turner7a5ee572011-02-02 15:58:45 +01002562 // Reply "OK" with the framebuffer's bits per pixel
2563 snprintf(reply_buf, sizeof(reply_buf), "OK: -bitsperpixel=%d\r\n",
2564 proxyFb_get_bits_per_pixel(core_fb));
2565 control_write( client, reply_buf);
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002566 return 0;
2567}
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -08002568
2569static int
2570do_create_user_events_service( ControlClient client, char* args )
2571{
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002572 // Make sure that there are no user events client already existing.
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -08002573 if (user_events_client != NULL) {
2574 control_write( client, "KO: Another user events service is already existing!\r\n" );
2575 control_client_destroy(client);
2576 return -1;
2577 }
2578
Vladimir Chtchetkine250b2e02011-01-28 10:56:16 -08002579 if (!userEventsImpl_create(client->sock)) {
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -08002580 char reply_buf[4096];
2581 user_events_client = client;
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -08002582 snprintf(reply_buf, sizeof(reply_buf), "OK\r\n");
2583 control_write( client, reply_buf);
2584 } else {
2585 control_write( client, "KO\r\n" );
2586 control_client_destroy(client);
2587 return -1;
2588 }
2589
2590 return 0;
2591}
Vladimir Chtchetkine6ee1c4e2011-01-20 11:22:32 -08002592
2593void
Vladimir Chtchetkine250b2e02011-01-28 10:56:16 -08002594destroy_user_events_client(void)
Vladimir Chtchetkine6ee1c4e2011-01-20 11:22:32 -08002595{
2596 if (user_events_client != NULL) {
2597 control_client_destroy(user_events_client);
2598 }
2599}
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002600
2601static int
2602do_create_ui_core_ctl_service( ControlClient client, char* args )
2603{
2604 // Make sure that there are no ui control client already existing.
2605 if (ui_core_ctl_client != NULL) {
2606 control_write( client, "KO: Another UI control service is already existing!\r\n" );
2607 control_client_destroy(client);
2608 return -1;
2609 }
2610
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -08002611 if (!coreCmdImpl_create(client->sock)) {
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002612 char reply_buf[4096];
2613 ui_core_ctl_client = client;
2614 snprintf(reply_buf, sizeof(reply_buf), "OK\r\n");
2615 control_write( client, reply_buf);
2616 } else {
2617 control_write( client, "KO\r\n" );
2618 control_client_destroy(client);
2619 return -1;
2620 }
2621
2622 return 0;
2623}
2624
2625void
2626destroy_ui_core_ctl_client(void)
2627{
2628 if (ui_core_ctl_client != NULL) {
2629 control_client_destroy(ui_core_ctl_client);
2630 }
2631}
2632
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -08002633void
2634destroy_corecmd_client(void)
2635{
2636 if (ui_core_ctl_client != NULL) {
2637 control_client_destroy(ui_core_ctl_client);
2638 }
2639}
2640
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002641static int
2642do_create_core_ui_ctl_service( ControlClient client, char* args )
2643{
2644 // Make sure that there are no ui control client already existing.
2645 if (core_ui_ctl_client != NULL) {
2646 control_write( client, "KO: Another UI control service is already existing!\r\n" );
2647 control_client_destroy(client);
2648 return -1;
2649 }
2650
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -08002651 if (!uiCmdProxy_create(client->sock)) {
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002652 char reply_buf[4096];
2653 core_ui_ctl_client = client;
2654 snprintf(reply_buf, sizeof(reply_buf), "OK\r\n");
2655 control_write( client, reply_buf);
2656 } else {
2657 control_write( client, "KO\r\n" );
2658 control_client_destroy(client);
2659 return -1;
2660 }
2661
2662 return 0;
2663}
2664
2665void
2666destroy_core_ui_ctl_client(void)
2667{
2668 if (core_ui_ctl_client != NULL) {
2669 control_client_destroy(core_ui_ctl_client);
2670 }
2671}
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -08002672
2673void
2674destroy_uicmd_client(void)
2675{
2676 if (core_ui_ctl_client != NULL) {
2677 control_client_destroy(core_ui_ctl_client);
2678 }
2679}
2680
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002681#endif // CONFIG_STANDALONE_CORE
2682
David 'Digit' Turnere92bc562010-09-07 06:21:25 -07002683static const CommandDefRec qemu_commands[] =
2684{
2685 { "monitor", "enter QEMU monitor",
2686 "Enter the QEMU virtual machine monitor\r\n",
2687 NULL, do_qemu_monitor, NULL },
2688
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002689#ifdef CONFIG_STANDALONE_CORE
Vladimir Chtchetkinea473d812011-01-26 08:53:05 -08002690 { "attach-UI", "attach UI to the core",
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002691 "Attach UI to the core\r\n",
2692 NULL, do_attach_ui, NULL },
2693
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002694 { "framebuffer", "create framebuffer service",
2695 "Create framebuffer service\r\n",
2696 NULL, do_create_framebuffer_service, NULL },
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -08002697
Vladimir Chtchetkinea473d812011-01-26 08:53:05 -08002698 { "user-events", "create user events service",
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -08002699 "Create user events service\r\n",
2700 NULL, do_create_user_events_service, NULL },
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002701
Vladimir Chtchetkinea473d812011-01-26 08:53:05 -08002702 { "ui-core-control", "create UI control service",
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002703 "Create UI control service\r\n",
2704 NULL, do_create_ui_core_ctl_service, NULL },
2705
Vladimir Chtchetkinea473d812011-01-26 08:53:05 -08002706 { "core-ui-control", "create UI control service",
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002707 "Create UI control service\r\n",
2708 NULL, do_create_core_ui_ctl_service, NULL },
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002709#endif // CONFIG_STANDALONE_CORE
2710
David 'Digit' Turnere92bc562010-09-07 06:21:25 -07002711 { NULL, NULL, NULL, NULL, NULL, NULL }
2712};
2713
2714
2715/********************************************************************************************/
2716/********************************************************************************************/
2717/***** ******/
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002718/***** M A I N C O M M A N D S ******/
2719/***** ******/
2720/********************************************************************************************/
2721/********************************************************************************************/
2722
2723static int
2724do_kill( ControlClient client, char* args )
2725{
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002726 control_write( client, "OK: killing emulator, bye bye\r\n" );
2727 exit(0);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002728}
2729
2730static const CommandDefRec main_commands[] =
2731{
2732 { "help|h|?", "print a list of commands", NULL, NULL, do_help, NULL },
2733
2734 { "event", "simulate hardware events",
2735 "allows you to send fake hardware events to the kernel\r\n", NULL,
2736 NULL, event_commands },
2737
2738 { "geo", "Geo-location commands",
2739 "allows you to change Geo-related settings, or to send GPS NMEA sentences\r\n", NULL,
2740 NULL, geo_commands },
2741
2742 { "gsm", "GSM related commands",
2743 "allows you to change GSM-related settings, or to make a new inbound phone call\r\n", NULL,
2744 NULL, gsm_commands },
2745
Jaime Lopez1a000852010-07-21 18:03:58 -07002746 { "cdma", "CDMA related commands",
2747 "allows you to change CDMA-related settings\r\n", NULL,
2748 NULL, cdma_commands },
2749
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002750 { "kill", "kill the emulator instance", NULL, NULL,
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002751 do_kill, NULL },
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002752
2753 { "network", "manage network settings",
2754 "allows you to manage the settings related to the network data connection of the\r\n"
2755 "emulated device.\r\n", NULL,
2756 NULL, network_commands },
2757
2758 { "power", "power related commands",
2759 "allows to change battery and AC power status\r\n", NULL,
2760 NULL, power_commands },
2761
2762 { "quit|exit", "quit control session", NULL, NULL,
2763 do_quit, NULL },
2764
2765 { "redir", "manage port redirections",
2766 "allows you to add, list and remove UDP and/or PORT redirection from the host to the device\r\n"
2767 "as an example, 'redir tcp:5000:6000' will route any packet sent to the host's TCP port 5000\r\n"
2768 "to TCP port 6000 of the emulated device\r\n", NULL,
2769 NULL, redir_commands },
2770
2771 { "sms", "SMS related commands",
2772 "allows you to simulate an inbound SMS\r\n", NULL,
2773 NULL, sms_commands },
2774
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002775 { "avd", "control virtual device execution",
2776 "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 -08002777 NULL, vm_commands },
2778
2779 { "window", "manage emulator window",
2780 "allows you to modify the emulator window\r\n", NULL,
2781 NULL, window_commands },
2782
David 'Digit' Turnere92bc562010-09-07 06:21:25 -07002783 { "qemu", "QEMU-specific commands",
2784 "allows to connect to the QEMU virtual machine monitor\r\n", NULL,
2785 NULL, qemu_commands },
2786
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002787 { NULL, NULL, NULL, NULL, NULL, NULL }
2788};
2789
2790
2791static ControlGlobalRec _g_global;
2792
2793int
2794control_console_start( int port )
2795{
2796 return control_global_init( &_g_global, port );
2797}