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