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