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