blob: ccd042603c792c793b0297d9050b32256a55b87a [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
David 'Digit' Turnercc330d42013-12-14 23:26:42 +010024#include "android/sockets.h"
David 'Digit' Turnere7216d82013-12-15 00:51:13 +010025#include "sysemu/char.h"
David 'Digit' Turner34c48ff2013-12-15 00:25:03 +010026#include "sysemu/sysemu.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080027#include "android/android.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080028#include "cpu.h"
David 'Digit' Turnerf5bc01c2013-12-17 10:33:07 +010029#include "hw/android/goldfish/device.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080030#include "hw/power_supply.h"
David 'Digit' Turnercc330d42013-12-14 23:26:42 +010031#include "android/shaper.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080032#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"
David 'Digit' Turneraf81d742014-02-03 17:11:18 +010037#include "android/utils/eintr_wrapper.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080038#include "android/utils/stralloc.h"
Ot ten Thije2ff39a32010-10-06 17:48:15 +010039#include "android/config/config.h"
David 'Digit' Turner6e2eb782013-12-15 00:54:21 +010040#include "android/tcpdump.h"
David 'Digit' Turnercc330d42013-12-14 23:26:42 +010041#include "net/net.h"
David 'Digit' Turner6af67652013-12-14 23:49:32 +010042#include "monitor/monitor.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080043
44#include <stdlib.h>
45#include <stdio.h>
46#include <stdarg.h>
47#include <string.h>
48#include <unistd.h>
49#include <fcntl.h>
50#include "android/hw-events.h"
David 'Digit' Turnerd4e803c2013-12-14 23:45:50 +010051#include "android/user-events.h"
Tim Wan736e01f2011-01-10 10:58:25 +010052#include "android/hw-sensors.h"
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -070053#include "android/keycode-array.h"
54#include "android/charmap.h"
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -080055#include "android/display-core.h"
Vladimir Chtchetkine94a2fba2011-01-31 10:49:06 -080056#include "android/protocol/fb-updates-proxy.h"
Vladimir Chtchetkine250b2e02011-01-28 10:56:16 -080057#include "android/protocol/user-events-impl.h"
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -080058#include "android/protocol/ui-commands-api.h"
59#include "android/protocol/core-commands-impl.h"
60#include "android/protocol/ui-commands-proxy.h"
Vladimir Chtchetkine85276802011-01-31 15:18:45 -080061#include "android/protocol/attach-ui-proxy.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080062
63#if defined(CONFIG_SLIRP)
64#include "libslirp.h"
65#endif
66
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080067#define DEBUG 1
68
69#if 1
70# define D_ACTIVE VERBOSE_CHECK(console)
71#else
72# define D_ACTIVE DEBUG
73#endif
74
75#if DEBUG
76# define D(x) do { if (D_ACTIVE) ( printf x , fflush(stdout) ); } while (0)
77#else
78# define D(x) do{}while(0)
79#endif
80
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080081typedef struct ControlGlobalRec_* ControlGlobal;
82
83typedef struct ControlClientRec_* ControlClient;
84
85typedef struct {
86 int host_port;
87 int host_udp;
88 unsigned int guest_ip;
89 int guest_port;
90} RedirRec, *Redir;
91
92
David 'Digit' Turnere92bc562010-09-07 06:21:25 -070093typedef int Socket;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080094
95typedef struct ControlClientRec_
96{
97 struct ControlClientRec_* next; /* next client in list */
98 Socket sock; /* socket used for communication */
99 ControlGlobal global;
100 char finished;
101 char buff[ 4096 ];
102 int buff_len;
103
104} ControlClientRec;
105
106
107typedef struct ControlGlobalRec_
108{
109 /* listening socket */
110 Socket listen_fd;
111
112 /* the list of current clients */
113 ControlClient clients;
114
115 /* the list of redirections currently active */
116 Redir redirs;
117 int num_redirs;
118 int max_redirs;
119
120} ControlGlobalRec;
121
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800122#ifdef CONFIG_STANDALONE_CORE
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -0800123/* UI client currently attached to the core. */
124ControlClient attached_ui_client = NULL;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800125
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -0800126/* 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;
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800137#endif // CONFIG_STANDALONE_CORE
138
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800139static int
140control_global_add_redir( ControlGlobal global,
141 int host_port,
142 int host_udp,
143 unsigned int guest_ip,
144 int guest_port )
145{
146 Redir redir;
147
148 if (global->num_redirs >= global->max_redirs)
149 {
150 int old_max = global->max_redirs;
151 int new_max = old_max + (old_max >> 1) + 4;
152
153 Redir new_redirs = realloc( global->redirs, new_max*sizeof(global->redirs[0]) );
154 if (new_redirs == NULL)
155 return -1;
156
157 global->redirs = new_redirs;
158 global->max_redirs = new_max;
159 }
160
161 redir = &global->redirs[ global->num_redirs++ ];
162
163 redir->host_port = host_port;
164 redir->host_udp = host_udp;
165 redir->guest_ip = guest_ip;
166 redir->guest_port = guest_port;
167
168 return 0;
169}
170
171static int
172control_global_del_redir( ControlGlobal global,
173 int host_port,
174 int host_udp )
175{
176 int nn;
177
178 for (nn = 0; nn < global->num_redirs; nn++)
179 {
180 Redir redir = &global->redirs[nn];
181
182 if ( redir->host_port == host_port &&
183 redir->host_udp == host_udp )
184 {
185 memmove( redir, redir + 1, ((global->num_redirs - nn)-1)*sizeof(*redir) );
186 global->num_redirs -= 1;
187 return 0;
188 }
189 }
190 /* we didn't find it */
191 return -1;
192}
193
David 'Digit' Turnere92bc562010-09-07 06:21:25 -0700194/* Detach the socket descriptor from a given ControlClient
195 * and return its value. This is useful either when destroying
196 * the client, or redirecting the socket to another service.
197 *
198 * NOTE: this does not close the socket.
199 */
200static int
201control_client_detach( ControlClient client )
202{
203 int result;
204
205 if (client->sock < 0)
206 return -1;
207
208 qemu_set_fd_handler( client->sock, NULL, NULL, NULL );
209 result = client->sock;
210 client->sock = -1;
211
212 return result;
213}
214
215static void control_client_read( void* _client ); /* forward */
216
217/* Reattach a control client to a given socket.
218 * Return the old socket descriptor for the client.
219 */
220static int
221control_client_reattach( ControlClient client, int fd )
222{
223 int result = control_client_detach(client);
224 client->sock = fd;
225 qemu_set_fd_handler( fd, control_client_read, NULL, client );
226 return result;
227}
228
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800229static void
230control_client_destroy( ControlClient client )
231{
232 ControlGlobal global = client->global;
233 ControlClient *pnode = &global->clients;
David 'Digit' Turnere92bc562010-09-07 06:21:25 -0700234 int sock;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800235
236 D(( "destroying control client %p\n", client ));
237
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800238#ifdef CONFIG_STANDALONE_CORE
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -0800239 if (client == attached_ui_client) {
Vladimir Chtchetkine85276802011-01-31 15:18:45 -0800240 attachUiProxy_destroy();
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -0800241 attached_ui_client = NULL;
242 }
243
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -0800244 if (client == user_events_client) {
Vladimir Chtchetkine250b2e02011-01-28 10:56:16 -0800245 userEventsImpl_destroy();
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -0800246 user_events_client = NULL;
247 }
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -0800248
249 if (client == ui_core_ctl_client) {
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800250 coreCmdImpl_destroy();
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -0800251 ui_core_ctl_client = NULL;
252 }
253
254 if (client == core_ui_ctl_client) {
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800255 uiCmdProxy_destroy();
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -0800256 core_ui_ctl_client = NULL;
257 }
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800258#endif // CONFIG_STANDALONE_CORE
259
David 'Digit' Turnere92bc562010-09-07 06:21:25 -0700260 sock = control_client_detach( client );
261 if (sock >= 0)
262 socket_close(sock);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800263
264 for ( ;; ) {
265 ControlClient node = *pnode;
266 if ( node == NULL )
267 break;
268 if ( node == client ) {
269 *pnode = node->next;
270 node->next = NULL;
271 break;
272 }
273 pnode = &node->next;
274 }
275
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800276 free( client );
277}
278
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800279
280
281static void control_control_write( ControlClient client, const char* buff, int len )
282{
283 int ret;
284
285 if (len < 0)
286 len = strlen(buff);
287
288 while (len > 0) {
David 'Digit' Turneraf81d742014-02-03 17:11:18 +0100289 ret = HANDLE_EINTR(socket_send( client->sock, buff, len));
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800290 if (ret < 0) {
David 'Digit' Turneraf81d742014-02-03 17:11:18 +0100291 if (errno != EWOULDBLOCK && errno != EAGAIN)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800292 return;
293 } else {
294 buff += ret;
295 len -= ret;
296 }
297 }
298}
299
Ot ten Thije2ff39a32010-10-06 17:48:15 +0100300static int control_vwrite( ControlClient client, const char* format, va_list args )
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800301{
302 static char temp[1024];
Ot ten Thije2ff39a32010-10-06 17:48:15 +0100303 int ret = vsnprintf( temp, sizeof(temp), format, args );
304 temp[ sizeof(temp)-1 ] = 0;
305 control_control_write( client, temp, -1 );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800306
Ot ten Thije2ff39a32010-10-06 17:48:15 +0100307 return ret;
308}
309
310static int control_write( ControlClient client, const char* format, ... )
311{
312 int ret;
313 va_list args;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800314 va_start(args, format);
Ot ten Thije2ff39a32010-10-06 17:48:15 +0100315 ret = control_vwrite(client, format, args);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800316 va_end(args);
317
Ot ten Thije2ff39a32010-10-06 17:48:15 +0100318 return ret;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800319}
320
321
322static ControlClient
323control_client_create( Socket socket,
324 ControlGlobal global )
325{
326 ControlClient client = calloc( sizeof(*client), 1 );
327
328 if (client) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800329 socket_set_nodelay( socket );
330 socket_set_nonblock( socket );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800331 client->finished = 0;
332 client->global = global;
333 client->sock = socket;
334 client->next = global->clients;
335 global->clients = client;
336
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800337 qemu_set_fd_handler( socket, control_client_read, NULL, client );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800338 }
339 return client;
340}
341
342typedef const struct CommandDefRec_ *CommandDef;
343
344typedef struct CommandDefRec_ {
345 const char* names;
346 const char* abstract;
347 const char* description;
348 void (*descriptor)( ControlClient client );
349 int (*handler)( ControlClient client, char* args );
350 CommandDef subcommands; /* if handler is NULL */
351
352} CommandDefRec;
353
354static const CommandDefRec main_commands[]; /* forward */
355
356static CommandDef
357find_command( char* input, CommandDef commands, char* *pend, char* *pargs )
358{
359 int nn;
360 char* args = strchr(input, ' ');
361
362 if (args != NULL) {
363 while (*args == ' ')
364 args++;
365
366 if (args[0] == 0)
367 args = NULL;
368 }
369
370 for (nn = 0; commands[nn].names != NULL; nn++)
371 {
372 const char* name = commands[nn].names;
373 const char* sep;
374
375 do {
376 int len, c;
377
378 sep = strchr( name, '|' );
379 if (sep)
380 len = sep - name;
381 else
382 len = strlen(name);
383
384 c = input[len];
385 if ( !memcmp( name, input, len ) && (c == ' ' || c == 0) ) {
386 *pend = input + len;
387 *pargs = args;
388 return &commands[nn];
389 }
390
391 if (sep)
392 name = sep + 1;
393
394 } while (sep != NULL && *name);
395 }
396 /* NOTE: don't touch *pend and *pargs if no command is found */
397 return NULL;
398}
399
400static void
401dump_help( ControlClient client,
402 CommandDef cmd,
403 const char* prefix )
404{
405 if (cmd->description) {
406 control_write( client, "%s", cmd->description );
407 } else if (cmd->descriptor) {
408 cmd->descriptor( client );
409 } else
410 control_write( client, "%s\r\n", cmd->abstract );
411
412 if (cmd->subcommands) {
413 cmd = cmd->subcommands;
414 control_write( client, "\r\navailable sub-commands:\r\n" );
415 for ( ; cmd->names != NULL; cmd++ ) {
416 control_write( client, " %s %-15s %s\r\n", prefix, cmd->names, cmd->abstract );
417 }
418 control_write( client, "\r\n" );
419 }
420}
421
422static void
423control_client_do_command( ControlClient client )
424{
425 char* line = client->buff;
426 char* args = NULL;
427 CommandDef commands = main_commands;
428 char* cmdend = client->buff;
429 CommandDef cmd = find_command( line, commands, &cmdend, &args );
430
431 if (cmd == NULL) {
432 control_write( client, "KO: unknown command, try 'help'\r\n" );
433 return;
434 }
435
436 for (;;) {
437 CommandDef subcmd;
438
439 if (cmd->handler) {
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -0800440 if ( !cmd->handler( client, args ) ) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800441 control_write( client, "OK\r\n" );
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -0800442 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800443 break;
444 }
445
446 /* no handler means we should have sub-commands */
447 if (cmd->subcommands == NULL) {
448 control_write( client, "KO: internal error: buggy command table for '%.*s'\r\n",
449 cmdend - client->buff, client->buff );
450 break;
451 }
452
453 /* we need a sub-command here */
454 if ( !args ) {
455 dump_help( client, cmd, "" );
456 control_write( client, "KO: missing sub-command\r\n" );
457 break;
458 }
459
460 line = args;
461 commands = cmd->subcommands;
462 subcmd = find_command( line, commands, &cmdend, &args );
463 if (subcmd == NULL) {
464 dump_help( client, cmd, "" );
465 control_write( client, "KO: bad sub-command\r\n" );
466 break;
467 }
468 cmd = subcmd;
469 }
470}
471
472/* implement the 'help' command */
473static int
474do_help( ControlClient client, char* args )
475{
476 char* line;
477 char* start = args;
478 char* end = start;
479 CommandDef cmd = main_commands;
480
481 /* without arguments, simply dump all commands */
482 if (args == NULL) {
483 control_write( client, "Android console command help:\r\n\r\n" );
484 for ( ; cmd->names != NULL; cmd++ ) {
485 control_write( client, " %-15s %s\r\n", cmd->names, cmd->abstract );
486 }
487 control_write( client, "\r\ntry 'help <command>' for command-specific help\r\n" );
488 return 0;
489 }
490
491 /* with an argument, find the corresponding command */
492 for (;;) {
493 CommandDef subcmd;
494
495 line = args;
496 subcmd = find_command( line, cmd, &end, &args );
497 if (subcmd == NULL) {
498 control_write( client, "try one of these instead:\r\n\r\n" );
499 for ( ; cmd->names != NULL; cmd++ ) {
500 control_write( client, " %.*s %s\r\n",
501 end - start, start, cmd->names );
502 }
503 control_write( client, "\r\nKO: unknown command\r\n" );
504 return -1;
505 }
506
507 if ( !args || !subcmd->subcommands ) {
508 dump_help( client, subcmd, start );
509 return 0;
510 }
511 cmd = subcmd->subcommands;
512 }
513}
514
515
516static void
517control_client_read_byte( ControlClient client, unsigned char ch )
518{
519 if (ch == '\r')
520 {
521 /* filter them out */
522 }
523 else if (ch == '\n')
524 {
525 client->buff[ client->buff_len ] = 0;
526 control_client_do_command( client );
527 if (client->finished)
528 return;
529
530 client->buff_len = 0;
531 }
532 else
533 {
534 if (client->buff_len >= sizeof(client->buff)-1)
535 client->buff_len = 0;
536
537 client->buff[ client->buff_len++ ] = ch;
538 }
539}
540
541static void
542control_client_read( void* _client )
543{
544 ControlClient client = _client;
545 unsigned char buf[4096];
546 int size;
547
548 D(( "in control_client read: " ));
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800549 size = socket_recv( client->sock, buf, sizeof(buf) );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800550 if (size < 0) {
551 D(( "size < 0, exiting with %d: %s\n", errno, errno_str ));
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -0800552 if (errno != EWOULDBLOCK && errno != EAGAIN)
553 control_client_destroy( client );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800554 return;
555 }
556
557 if (size == 0) {
558 /* end of connection */
559 D(( "end of connection detected !!\n" ));
560 control_client_destroy( client );
561 }
562 else {
563 int nn;
564#ifdef _WIN32
565# if DEBUG
566 char temp[16];
567 int count = size > sizeof(temp)-1 ? sizeof(temp)-1 : size;
568 for (nn = 0; nn < count; nn++) {
569 int c = buf[nn];
570 if (c == '\n')
571 temp[nn] = '!';
572 else if (c < 32)
573 temp[nn] = '.';
574 else
575 temp[nn] = (char)c;
576 }
577 temp[nn] = 0;
578 D(( "received %d bytes: %s\n", size, temp ));
579# endif
580#else
581 D(( "received %.*s\n", size, buf ));
582#endif
583 for (nn = 0; nn < size; nn++) {
584 control_client_read_byte( client, buf[nn] );
585 if (client->finished) {
586 control_client_destroy(client);
587 return;
588 }
589 }
590 }
591}
592
593
594/* this function is called on each new client connection */
595static void
596control_global_accept( void* _global )
597{
598 ControlGlobal global = _global;
599 ControlClient client;
600 Socket fd;
601
David 'Digit' Turner80bc5c82010-10-20 19:04:51 +0200602 D(( "control_global_accept: just in (fd=%d)\n", global->listen_fd ));
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800603
David 'Digit' Turneraf81d742014-02-03 17:11:18 +0100604 fd = HANDLE_EINTR(socket_accept(global->listen_fd, NULL));
605 if (fd < 0) {
606 D(( "problem in accept: %d: %s\n", errno, errno_str ));
607 perror("accept");
608 return;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800609 }
610
611 socket_set_xreuseaddr( fd );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800612
613 D(( "control_global_accept: creating new client\n" ));
614 client = control_client_create( fd, global );
615 if (client) {
616 D(( "control_global_accept: new client %p\n", client ));
617 control_write( client, "Android Console: type 'help' for a list of commands\r\n" );
618 control_write( client, "OK\r\n" );
619 }
620}
621
622
623static int
624control_global_init( ControlGlobal global,
625 int control_port )
626{
627 Socket fd;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800628 int ret;
629 SockAddress sockaddr;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800630
631 memset( global, 0, sizeof(*global) );
632
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800633 fd = socket_create_inet( SOCKET_STREAM );
634 if (fd < 0) {
635 perror("socket");
636 return -1;
637 }
638
639 socket_set_xreuseaddr( fd );
640
641 sock_address_init_inet( &sockaddr, SOCK_ADDRESS_INET_LOOPBACK, control_port );
642
643 ret = socket_bind(fd, &sockaddr );
644 if (ret < 0) {
645 perror("bind");
646 socket_close( fd );
647 return -1;
648 }
649
650 ret = socket_listen(fd, 0);
651 if (ret < 0) {
652 perror("listen");
653 socket_close( fd );
654 return -1;
655 }
656
657 socket_set_nonblock(fd);
658
659 global->listen_fd = fd;
660
661 qemu_set_fd_handler( fd, control_global_accept, NULL, global );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800662 return 0;
663}
664
665
666
667static int
668do_quit( ControlClient client, char* args )
669{
670 client->finished = 1;
671 return -1;
672}
673
674/********************************************************************************************/
675/********************************************************************************************/
676/***** ******/
677/***** N E T W O R K S E T T I N G S ******/
678/***** ******/
679/********************************************************************************************/
680/********************************************************************************************/
681
682static int
683do_network_status( ControlClient client, char* args )
684{
685 control_write( client, "Current network status:\r\n" );
686
687 control_write( client, " download speed: %8d bits/s (%.1f KB/s)\r\n",
688 (long)qemu_net_download_speed, qemu_net_download_speed/8192. );
689
690 control_write( client, " upload speed: %8d bits/s (%.1f KB/s)\r\n",
691 (long)qemu_net_upload_speed, qemu_net_upload_speed/8192. );
692
693 control_write( client, " minimum latency: %ld ms\r\n", qemu_net_min_latency );
694 control_write( client, " maximum latency: %ld ms\r\n", qemu_net_max_latency );
695 return 0;
696}
697
698static void
699dump_network_speeds( ControlClient client )
700{
701 const NetworkSpeed* speed = android_netspeeds;
702 const char* const format = " %-8s %s\r\n";
703 for ( ; speed->name; speed++ ) {
704 control_write( client, format, speed->name, speed->display );
705 }
706 control_write( client, format, "<num>", "selects both upload and download speed" );
707 control_write( client, format, "<up>:<down>", "select individual upload/download speeds" );
708}
709
710
711static int
712do_network_speed( ControlClient client, char* args )
713{
714 if ( !args ) {
715 control_write( client, "KO: missing <speed> argument, see 'help network speed'\r\n" );
716 return -1;
717 }
718 if ( android_parse_network_speed( args ) < 0 ) {
719 control_write( client, "KO: invalid <speed> argument, see 'help network speed' for valid values\r\n" );
720 return -1;
721 }
722
723 netshaper_set_rate( slirp_shaper_in, qemu_net_download_speed );
724 netshaper_set_rate( slirp_shaper_out, qemu_net_upload_speed );
725
726 if (android_modem) {
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -0700727 amodem_set_data_network_type( android_modem,
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800728 android_parse_network_type( args ) );
729 }
730 return 0;
731}
732
733static void
734describe_network_speed( ControlClient client )
735{
736 control_write( client,
737 "'network speed <speed>' allows you to dynamically change the speed of the emulated\r\n"
738 "network on the device, where <speed> is one of the following:\r\n\r\n" );
739 dump_network_speeds( client );
740}
741
742static int
743do_network_delay( ControlClient client, char* args )
744{
745 if ( !args ) {
746 control_write( client, "KO: missing <delay> argument, see 'help network delay'\r\n" );
747 return -1;
748 }
749 if ( android_parse_network_latency( args ) < 0 ) {
750 control_write( client, "KO: invalid <delay> argument, see 'help network delay' for valid values\r\n" );
751 return -1;
752 }
753 netdelay_set_latency( slirp_delay_in, qemu_net_min_latency, qemu_net_max_latency );
754 return 0;
755}
756
757static void
758describe_network_delay( ControlClient client )
759{
760 control_write( client,
761 "'network delay <latency>' allows you to dynamically change the latency of the emulated\r\n"
762 "network on the device, where <latency> is one of the following:\r\n\r\n" );
763 /* XXX: TODO */
764}
765
766static int
767do_network_capture_start( ControlClient client, char* args )
768{
769 if ( !args ) {
770 control_write( client, "KO: missing <file> argument, see 'help network capture start'\r\n" );
771 return -1;
772 }
773 if ( qemu_tcpdump_start(args) < 0) {
774 control_write( client, "KO: could not start capture: %s", strerror(errno) );
775 return -1;
776 }
777 return 0;
778}
779
780static int
781do_network_capture_stop( ControlClient client, char* args )
782{
783 /* no need to return an error here */
784 qemu_tcpdump_stop();
785 return 0;
786}
787
788static const CommandDefRec network_capture_commands[] =
789{
790 { "start", "start network capture",
791 "'network capture start <file>' starts a new capture of network packets\r\n"
792 "into a specific <file>. This will stop any capture already in progress.\r\n"
793 "the capture file can later be analyzed by tools like WireShark. It uses\r\n"
794 "the libpcap file format.\r\n\r\n"
795 "you can stop the capture anytime with 'network capture stop'\r\n", NULL,
796 do_network_capture_start, NULL },
797
798 { "stop", "stop network capture",
799 "'network capture stop' stops a currently running packet capture, if any.\r\n"
800 "you can start one with 'network capture start <file>'\r\n", NULL,
801 do_network_capture_stop, NULL },
802
803 { NULL, NULL, NULL, NULL, NULL, NULL }
804};
805
806static const CommandDefRec network_commands[] =
807{
808 { "status", "dump network status", NULL, NULL,
809 do_network_status, NULL },
810
811 { "speed", "change network speed", NULL, describe_network_speed,
812 do_network_speed, NULL },
813
814 { "delay", "change network latency", NULL, describe_network_delay,
815 do_network_delay, NULL },
816
817 { "capture", "dump network packets to file",
818 "allows to start/stop capture of network packets to a file for later analysis\r\n", NULL,
819 NULL, network_capture_commands },
820
821 { NULL, NULL, NULL, NULL, NULL, NULL }
822};
823
824/********************************************************************************************/
825/********************************************************************************************/
826/***** ******/
827/***** P O R T R E D I R E C T I O N S ******/
828/***** ******/
829/********************************************************************************************/
830/********************************************************************************************/
831
832static int
833do_redir_list( ControlClient client, char* args )
834{
835 ControlGlobal global = client->global;
836
837 if (global->num_redirs == 0)
838 control_write( client, "no active redirections\r\n" );
839 else {
840 int nn;
841 for (nn = 0; nn < global->num_redirs; nn++) {
842 Redir redir = &global->redirs[nn];
843 control_write( client, "%s:%-5d => %-5d\r\n",
844 redir->host_udp ? "udp" : "tcp",
845 redir->host_port,
846 redir->guest_port );
847 }
848 }
849 return 0;
850}
851
852/* parse a protocol:port specification */
853static int
854redir_parse_proto_port( char* args, int *pport, int *pproto )
855{
856 int proto = -1;
857 int len = 0;
858 char* end;
859
860 if ( !memcmp( args, "tcp:", 4 ) ) {
861 proto = 0;
862 len = 4;
863 }
864 else if ( !memcmp( args, "udp:", 4 ) ) {
865 proto = 1;
866 len = 4;
867 }
868 else
869 return 0;
870
871 args += len;
872 *pproto = proto;
873 *pport = strtol( args, &end, 10 );
874 if (end == args)
875 return 0;
876
877 len += end - args;
878 return len;
879}
880
881static int
882redir_parse_guest_port( char* arg, int *pport )
883{
884 char* end;
885
886 *pport = strtoul( arg, &end, 10 );
887 if (end == arg)
888 return 0;
889
890 return end - arg;
891}
892
893static Redir
894redir_find( ControlGlobal global, int port, int isudp )
895{
896 int nn;
897
898 for (nn = 0; nn < global->num_redirs; nn++) {
899 Redir redir = &global->redirs[nn];
900
901 if (redir->host_port == port && redir->host_udp == isudp)
902 return redir;
903 }
904 return NULL;
905}
906
907
908static int
909do_redir_add( ControlClient client, char* args )
910{
911 int len, host_proto, host_port, guest_port;
912 uint32_t guest_ip;
913 Redir redir;
914
915 if ( !args )
916 goto BadFormat;
917
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700918 if (!slirp_is_inited()) {
919 control_write( client, "KO: network emulation disabled\r\n");
920 return -1;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800921 }
922
923 len = redir_parse_proto_port( args, &host_port, &host_proto );
924 if (len == 0 || args[len] != ':')
925 goto BadFormat;
926
927 args += len + 1;
928 len = redir_parse_guest_port( args, &guest_port );
929 if (len == 0 || args[len] != 0)
930 goto BadFormat;
931
932 redir = redir_find( client->global, host_port, host_proto );
933 if ( redir != NULL ) {
934 control_write( client, "KO: host port already active, use 'redir del' to remove first\r\n" );
935 return -1;
936 }
937
David Turner7d9a2702009-04-14 14:43:24 -0700938 if (inet_strtoip("10.0.2.15", &guest_ip) < 0) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800939 control_write( client, "KO: unexpected internal failure when resolving 10.0.2.15\r\n" );
940 return -1;
941 }
942
943 D(("pattern hport=%d gport=%d proto=%d\n", host_port, guest_port, host_proto ));
944 if ( control_global_add_redir( client->global, host_port, host_proto,
945 guest_ip, guest_port ) < 0 )
946 {
947 control_write( client, "KO: not enough memory to allocate redirection\r\n" );
948 return -1;
949 }
950
951 if (slirp_redir(host_proto, host_port, guest_ip, guest_port) < 0) {
952 control_write( client, "KO: can't setup redirection, port probably used by another program on host\r\n" );
953 control_global_del_redir( client->global, host_port, host_proto );
954 return -1;
955 }
956
957 return 0;
958
959BadFormat:
960 control_write( client, "KO: bad redirection format, try (tcp|udp):hostport:guestport\r\n", -1 );
961 return -1;
962}
963
964
965static int
966do_redir_del( ControlClient client, char* args )
967{
968 int len, proto, port;
969 Redir redir;
970
971 if ( !args )
972 goto BadFormat;
973 len = redir_parse_proto_port( args, &port, &proto );
974 if ( len == 0 || args[len] != 0 )
975 goto BadFormat;
976
977 redir = redir_find( client->global, port, proto );
978 if (redir == NULL) {
979 control_write( client, "KO: can't remove unknown redirection (%s:%d)\r\n",
980 proto ? "udp" : "tcp", port );
981 return -1;
982 }
983
984 slirp_unredir( redir->host_udp, redir->host_port );
985 control_global_del_redir( client->global, port, proto );\
986
987 return 0;
988
989BadFormat:
990 control_write( client, "KO: bad redirection format, try (tcp|udp):hostport\r\n" );
991 return -1;
992}
993
994static const CommandDefRec redir_commands[] =
995{
996 { "list", "list current redirections",
997 "list current port redirections. use 'redir add' and 'redir del' to add and remove them\r\n", NULL,
998 do_redir_list, NULL },
999
1000 { "add", "add new redirection",
1001 "add a new port redirection, arguments must be:\r\n\r\n"
1002 " redir add <protocol>:<host-port>:<guest-port>\r\n\r\n"
1003 "where: <protocol> is either 'tcp' or 'udp'\r\n"
1004 " <host-port> a number indicating which port on the host to open\r\n"
1005 " <guest-port> a number indicating which port to route to on the device\r\n"
1006 "\r\nas an example, 'redir tcp:5000:6000' will allow any packets sent to\r\n"
1007 "the host's TCP port 5000 to be routed to TCP port 6000 of the emulated device\r\n", NULL,
1008 do_redir_add, NULL },
1009
1010 { "del", "remove existing redirection",
1011 "remove a port redirecion that was created with 'redir add', arguments must be:\r\n\r\n"
1012 " redir del <protocol>:<host-port>\r\n\r\n"
1013 "see the 'help redir add' for the meaning of <protocol> and <host-port>\r\n", NULL,
1014 do_redir_del, NULL },
1015
1016 { NULL, NULL, NULL, NULL, NULL, NULL }
1017};
1018
1019
1020
1021/********************************************************************************************/
1022/********************************************************************************************/
1023/***** ******/
Jaime Lopez1a000852010-07-21 18:03:58 -07001024/***** C D M A M O D E M ******/
1025/***** ******/
1026/********************************************************************************************/
1027/********************************************************************************************/
1028
1029static const struct {
1030 const char * name;
1031 const char * display;
1032 ACdmaSubscriptionSource source;
1033} _cdma_subscription_sources[] = {
1034 { "nv", "Read subscription from non-volatile RAM", A_SUBSCRIPTION_NVRAM },
1035 { "ruim", "Read subscription from RUIM", A_SUBSCRIPTION_RUIM },
1036};
1037
1038static void
1039dump_subscription_sources( ControlClient client )
1040{
1041 int i;
1042 for (i = 0;
1043 i < sizeof(_cdma_subscription_sources) / sizeof(_cdma_subscription_sources[0]);
1044 i++) {
1045 control_write( client, " %s: %s\r\n",
1046 _cdma_subscription_sources[i].name,
1047 _cdma_subscription_sources[i].display );
1048 }
1049}
1050
1051static void
1052describe_subscription_source( ControlClient client )
1053{
1054 control_write( client,
1055 "'cdma ssource <ssource>' allows you to specify where to read the subscription from\r\n" );
1056 dump_subscription_sources( client );
1057}
1058
1059static int
1060do_cdma_ssource( ControlClient client, char* args )
1061{
1062 int nn;
1063 if (!args) {
1064 control_write( client, "KO: missing argument, try 'cdma ssource <source>'\r\n" );
1065 return -1;
1066 }
1067
1068 for (nn = 0; ; nn++) {
1069 const char* name = _cdma_subscription_sources[nn].name;
1070 ACdmaSubscriptionSource ssource = _cdma_subscription_sources[nn].source;
1071
1072 if (!name)
1073 break;
1074
1075 if (!strcasecmp( args, name )) {
1076 amodem_set_cdma_subscription_source( android_modem, ssource );
1077 return 0;
1078 }
1079 }
1080 control_write( client, "KO: Don't know source %s\r\n", args );
1081 return -1;
1082}
1083
1084static int
1085do_cdma_prl_version( ControlClient client, char * args )
1086{
1087 int version = 0;
1088 char *endptr;
1089
1090 if (!args) {
1091 control_write( client, "KO: missing argument, try 'cdma prl_version <version>'\r\n");
1092 return -1;
1093 }
1094
1095 version = strtol(args, &endptr, 0);
1096 if (endptr != args) {
1097 amodem_set_cdma_prl_version( android_modem, version );
1098 }
David 'Digit' Turner80bc5c82010-10-20 19:04:51 +02001099 return 0;
Jaime Lopez1a000852010-07-21 18:03:58 -07001100}
1101/********************************************************************************************/
1102/********************************************************************************************/
1103/***** ******/
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001104/***** G S M M O D E M ******/
1105/***** ******/
1106/********************************************************************************************/
1107/********************************************************************************************/
1108
1109static const struct {
1110 const char* name;
1111 const char* display;
1112 ARegistrationState state;
1113} _gsm_states[] = {
1114 { "unregistered", "no network available", A_REGISTRATION_UNREGISTERED },
1115 { "home", "on local network, non-roaming", A_REGISTRATION_HOME },
1116 { "roaming", "on roaming network", A_REGISTRATION_ROAMING },
1117 { "searching", "searching networks", A_REGISTRATION_SEARCHING },
1118 { "denied", "emergency calls only", A_REGISTRATION_DENIED },
1119 { "off", "same as 'unregistered'", A_REGISTRATION_UNREGISTERED },
1120 { "on", "same as 'home'", A_REGISTRATION_HOME },
1121 { NULL, NULL, A_REGISTRATION_UNREGISTERED }
1122};
1123
1124static const char*
1125gsm_state_to_string( ARegistrationState state )
1126{
1127 int nn;
1128 for (nn = 0; _gsm_states[nn].name != NULL; nn++) {
1129 if (state == _gsm_states[nn].state)
1130 return _gsm_states[nn].name;
1131 }
1132 return "<unknown>";
1133}
1134
1135static int
1136do_gsm_status( ControlClient client, char* args )
1137{
1138 if (args) {
1139 control_write( client, "KO: no argument required\r\n" );
1140 return -1;
1141 }
1142 if (!android_modem) {
1143 control_write( client, "KO: modem emulation not running\r\n" );
1144 return -1;
1145 }
1146 control_write( client, "gsm voice state: %s\r\n",
1147 gsm_state_to_string(
1148 amodem_get_voice_registration(android_modem) ) );
1149 control_write( client, "gsm data state: %s\r\n",
1150 gsm_state_to_string(
1151 amodem_get_data_registration(android_modem) ) );
1152 return 0;
1153}
1154
1155
1156static void
1157help_gsm_data( ControlClient client )
1158{
1159 int nn;
1160 control_write( client,
1161 "the 'gsm data <state>' allows you to change the state of your GPRS connection\r\n"
1162 "valid values for <state> are the following:\r\n\r\n" );
1163 for (nn = 0; ; nn++) {
1164 const char* name = _gsm_states[nn].name;
1165 const char* display = _gsm_states[nn].display;
1166
1167 if (!name)
1168 break;
1169
1170 control_write( client, " %-15s %s\r\n", name, display );
1171 }
1172 control_write( client, "\r\n" );
1173}
1174
1175
1176static int
1177do_gsm_data( ControlClient client, char* args )
1178{
1179 int nn;
1180
1181 if (!args) {
1182 control_write( client, "KO: missing argument, try 'gsm data <state>'\r\n" );
1183 return -1;
1184 }
1185
1186 for (nn = 0; ; nn++) {
1187 const char* name = _gsm_states[nn].name;
1188 ARegistrationState state = _gsm_states[nn].state;
1189
1190 if (!name)
1191 break;
1192
1193 if ( !strcmp( args, name ) ) {
1194 if (!android_modem) {
1195 control_write( client, "KO: modem emulation not running\r\n" );
1196 return -1;
1197 }
1198 amodem_set_data_registration( android_modem, state );
1199 qemu_net_disable = (state != A_REGISTRATION_HOME &&
1200 state != A_REGISTRATION_ROAMING );
1201 return 0;
1202 }
1203 }
1204 control_write( client, "KO: bad GSM data state name, try 'help gsm data' for list of valid values\r\n" );
1205 return -1;
1206}
1207
1208static void
1209help_gsm_voice( ControlClient client )
1210{
1211 int nn;
1212 control_write( client,
1213 "the 'gsm voice <state>' allows you to change the state of your GPRS connection\r\n"
1214 "valid values for <state> are the following:\r\n\r\n" );
1215 for (nn = 0; ; nn++) {
1216 const char* name = _gsm_states[nn].name;
1217 const char* display = _gsm_states[nn].display;
1218
1219 if (!name)
1220 break;
1221
1222 control_write( client, " %-15s %s\r\n", name, display );
1223 }
1224 control_write( client, "\r\n" );
1225}
1226
1227
1228static int
1229do_gsm_voice( ControlClient client, char* args )
1230{
1231 int nn;
1232
1233 if (!args) {
1234 control_write( client, "KO: missing argument, try 'gsm voice <state>'\r\n" );
1235 return -1;
1236 }
1237
1238 for (nn = 0; ; nn++) {
1239 const char* name = _gsm_states[nn].name;
1240 ARegistrationState state = _gsm_states[nn].state;
1241
1242 if (!name)
1243 break;
1244
1245 if ( !strcmp( args, name ) ) {
1246 if (!android_modem) {
1247 control_write( client, "KO: modem emulation not running\r\n" );
1248 return -1;
1249 }
1250 amodem_set_voice_registration( android_modem, state );
1251 return 0;
1252 }
1253 }
1254 control_write( client, "KO: bad GSM data state name, try 'help gsm voice' for list of valid values\r\n" );
1255 return -1;
1256}
1257
1258
1259static int
1260gsm_check_number( char* args )
1261{
1262 int nn;
1263
1264 for (nn = 0; args[nn] != 0; nn++) {
1265 int c = args[nn];
1266 if ( !isdigit(c) && c != '+' && c != '#' ) {
1267 return -1;
1268 }
1269 }
1270 if (nn == 0)
1271 return -1;
1272
1273 return 0;
1274}
1275
1276static int
1277do_gsm_call( ControlClient client, char* args )
1278{
1279 /* check that we have a phone number made of digits */
1280 if (!args) {
1281 control_write( client, "KO: missing argument, try 'gsm call <phonenumber>'\r\n" );
1282 return -1;
1283 }
1284
1285 if (gsm_check_number(args)) {
1286 control_write( client, "KO: bad phone number format, use digits, # and + only\r\n" );
1287 return -1;
1288 }
1289
1290 if (!android_modem) {
1291 control_write( client, "KO: modem emulation not running\r\n" );
1292 return -1;
1293 }
1294 amodem_add_inbound_call( android_modem, args );
1295 return 0;
1296}
1297
1298static int
1299do_gsm_cancel( ControlClient client, char* args )
1300{
1301 if (!args) {
1302 control_write( client, "KO: missing argument, try 'gsm call <phonenumber>'\r\n" );
1303 return -1;
1304 }
1305 if (gsm_check_number(args)) {
1306 control_write( client, "KO: bad phone number format, use digits, # and + only\r\n" );
1307 return -1;
1308 }
1309 if (!android_modem) {
1310 control_write( client, "KO: modem emulation not running\r\n" );
1311 return -1;
1312 }
1313 if ( amodem_disconnect_call( android_modem, args ) < 0 ) {
1314 control_write( client, "KO: could not cancel this number\r\n" );
1315 return -1;
1316 }
1317 return 0;
1318}
1319
1320
1321static const char*
1322call_state_to_string( ACallState state )
1323{
1324 switch (state) {
1325 case A_CALL_ACTIVE: return "active";
1326 case A_CALL_HELD: return "held";
1327 case A_CALL_ALERTING: return "ringing";
1328 case A_CALL_WAITING: return "waiting";
1329 case A_CALL_INCOMING: return "incoming";
1330 default: return "unknown";
1331 }
1332}
1333
1334static int
1335do_gsm_list( ControlClient client, char* args )
1336{
1337 /* check that we have a phone number made of digits */
1338 int count = amodem_get_call_count( android_modem );
1339 int nn;
1340 for (nn = 0; nn < count; nn++) {
1341 ACall call = amodem_get_call( android_modem, nn );
1342 const char* dir;
1343
1344 if (call == NULL)
1345 continue;
1346
1347 if (call->dir == A_CALL_OUTBOUND)
1348 dir = "outbound to ";
1349 else
1350 dir = "inbound from";
1351
1352 control_write( client, "%s %-10s : %s\r\n", dir,
1353 call->number, call_state_to_string(call->state) );
1354 }
1355 return 0;
1356}
1357
1358static int
1359do_gsm_busy( ControlClient client, char* args )
1360{
1361 ACall call;
1362
1363 if (!args) {
1364 control_write( client, "KO: missing argument, try 'gsm busy <phonenumber>'\r\n" );
1365 return -1;
1366 }
1367 call = amodem_find_call_by_number( android_modem, args );
1368 if (call == NULL || call->dir != A_CALL_OUTBOUND) {
1369 control_write( client, "KO: no current outbound call to number '%s' (call %p)\r\n", args, call );
1370 return -1;
1371 }
1372 if ( amodem_disconnect_call( android_modem, args ) < 0 ) {
1373 control_write( client, "KO: could not cancel this number\r\n" );
1374 return -1;
1375 }
1376 return 0;
1377}
1378
1379static int
1380do_gsm_hold( ControlClient client, char* args )
1381{
1382 ACall call;
1383
1384 if (!args) {
1385 control_write( client, "KO: missing argument, try 'gsm out hold <phonenumber>'\r\n" );
1386 return -1;
1387 }
1388 call = amodem_find_call_by_number( android_modem, args );
1389 if (call == NULL) {
1390 control_write( client, "KO: no current call to/from number '%s'\r\n", args );
1391 return -1;
1392 }
1393 if ( amodem_update_call( android_modem, args, A_CALL_HELD ) < 0 ) {
1394 control_write( client, "KO: could put this call on hold\r\n" );
1395 return -1;
1396 }
1397 return 0;
1398}
1399
1400
1401static int
1402do_gsm_accept( ControlClient client, char* args )
1403{
1404 ACall call;
1405
1406 if (!args) {
1407 control_write( client, "KO: missing argument, try 'gsm accept <phonenumber>'\r\n" );
1408 return -1;
1409 }
1410 call = amodem_find_call_by_number( android_modem, args );
1411 if (call == NULL) {
1412 control_write( client, "KO: no current call to/from number '%s'\r\n", args );
1413 return -1;
1414 }
1415 if ( amodem_update_call( android_modem, args, A_CALL_ACTIVE ) < 0 ) {
1416 control_write( client, "KO: could not activate this call\r\n" );
1417 return -1;
1418 }
1419 return 0;
1420}
1421
Tim Baverstock4c6b10a2010-12-15 17:31:13 +00001422static int
1423do_gsm_signal( ControlClient client, char* args )
1424{
1425 enum { SIGNAL_RSSI = 0, SIGNAL_BER, NUM_SIGNAL_PARAMS };
1426 char* p = args;
1427 int top_param = -1;
1428 int params[ NUM_SIGNAL_PARAMS ];
1429
1430 static int last_ber = 99;
1431
1432 if (!p)
1433 p = "";
1434
1435 /* tokenize */
1436 while (*p) {
1437 char* end;
1438 int val = strtol( p, &end, 10 );
1439
1440 if (end == p) {
1441 control_write( client, "KO: argument '%s' is not a number\n", p );
1442 return -1;
1443 }
1444
1445 params[++top_param] = val;
1446 if (top_param + 1 == NUM_SIGNAL_PARAMS)
1447 break;
1448
1449 p = end;
1450 while (*p && (p[0] == ' ' || p[0] == '\t'))
1451 p += 1;
1452 }
1453
1454 /* sanity check */
1455 if (top_param < SIGNAL_RSSI) {
1456 control_write( client, "KO: not enough arguments: see 'help gsm signal' for details\r\n" );
1457 return -1;
1458 }
1459
1460 int rssi = params[SIGNAL_RSSI];
1461 if ((rssi < 0 || rssi > 31) && rssi != 99) {
1462 control_write( client, "KO: invalid RSSI - must be 0..31 or 99\r\n");
1463 return -1;
1464 }
1465
1466 /* check ber is 0..7 or 99 */
1467 if (top_param >= SIGNAL_BER) {
1468 int ber = params[SIGNAL_BER];
1469 if ((ber < 0 || ber > 7) && ber != 99) {
1470 control_write( client, "KO: invalid BER - must be 0..7 or 99\r\n");
1471 return -1;
1472 }
1473 last_ber = ber;
1474 }
1475
1476 amodem_set_signal_strength( android_modem, rssi, last_ber );
1477
1478 return 0;
1479 }
1480
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001481
1482#if 0
1483static const CommandDefRec gsm_in_commands[] =
1484{
1485 { "new", "create a new 'waiting' inbound call",
1486 "'gsm in create <phonenumber>' creates a new inbound phone call, placed in\r\n"
1487 "the 'waiting' state by default, until the system answers/holds/closes it\r\n", NULL
1488 do_gsm_in_create, NULL },
1489
1490 { "hold", "change the state of an oubtound call to 'held'",
1491 "change the state of an outbound call to 'held'. this is only possible\r\n"
1492 "if the call in the 'waiting' or 'active' state\r\n", NULL,
1493 do_gsm_out_hold, NULL },
1494
1495 { "accept", "change the state of an outbound call to 'active'",
1496 "change the state of an outbound call to 'active'. this is only possible\r\n"
1497 "if the call is in the 'waiting' or 'held' state\r\n", NULL,
1498 do_gsm_out_accept, NULL },
1499
1500 { NULL, NULL, NULL, NULL, NULL, NULL }
1501};
1502#endif
1503
1504
Jaime Lopez1a000852010-07-21 18:03:58 -07001505static const CommandDefRec cdma_commands[] =
1506{
1507 { "ssource", "Set the current CDMA subscription source",
1508 NULL, describe_subscription_source,
1509 do_cdma_ssource, NULL },
David 'Digit' Turner80bc5c82010-10-20 19:04:51 +02001510 { "prl_version", "Dump the current PRL version",
1511 NULL, NULL,
1512 do_cdma_prl_version, NULL },
Jaime Lopez1a000852010-07-21 18:03:58 -07001513};
1514
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001515static const CommandDefRec gsm_commands[] =
1516{
1517 { "list", "list current phone calls",
1518 "'gsm list' lists all inbound and outbound calls and their state\r\n", NULL,
1519 do_gsm_list, NULL },
1520
1521 { "call", "create inbound phone call",
1522 "'gsm call <phonenumber>' allows you to simulate a new inbound call\r\n", NULL,
1523 do_gsm_call, NULL },
1524
1525 { "busy", "close waiting outbound call as busy",
1526 "'gsm busy <remoteNumber>' closes an outbound call, reporting\r\n"
1527 "the remote phone as busy. only possible if the call is 'waiting'.\r\n", NULL,
1528 do_gsm_busy, NULL },
1529
1530 { "hold", "change the state of an oubtound call to 'held'",
1531 "'gsm hold <remoteNumber>' change the state of a call to 'held'. this is only possible\r\n"
1532 "if the call in the 'waiting' or 'active' state\r\n", NULL,
1533 do_gsm_hold, NULL },
1534
1535 { "accept", "change the state of an outbound call to 'active'",
1536 "'gsm accept <remoteNumber>' change the state of a call to 'active'. this is only possible\r\n"
1537 "if the call is in the 'waiting' or 'held' state\r\n", NULL,
1538 do_gsm_accept, NULL },
1539
1540 { "cancel", "disconnect an inbound or outbound phone call",
1541 "'gsm cancel <phonenumber>' allows you to simulate the end of an inbound or outbound call\r\n", NULL,
1542 do_gsm_cancel, NULL },
1543
1544 { "data", "modify data connection state", NULL, help_gsm_data,
1545 do_gsm_data, NULL },
1546
1547 { "voice", "modify voice connection state", NULL, help_gsm_voice,
1548 do_gsm_voice, NULL },
1549
1550 { "status", "display GSM status",
1551 "'gsm status' displays the current state of the GSM emulation\r\n", NULL,
1552 do_gsm_status, NULL },
1553
Tim Baverstock4c6b10a2010-12-15 17:31:13 +00001554 { "signal", "set sets the rssi and ber",
1555 "'gsm signal <rssi> [<ber>]' changes the reported strength and error rate on next (15s) update.\r\n"
1556 "rssi range is 0..31 and 99 for unknown\r\n"
1557 "ber range is 0..7 percent and 99 for unknown\r\n",
1558 NULL, do_gsm_signal, NULL },
1559
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001560 { NULL, NULL, NULL, NULL, NULL, NULL }
1561};
1562
1563/********************************************************************************************/
1564/********************************************************************************************/
1565/***** ******/
1566/***** S M S C O M M A N D ******/
1567/***** ******/
1568/********************************************************************************************/
1569/********************************************************************************************/
1570
1571static int
1572do_sms_send( ControlClient client, char* args )
1573{
1574 char* p;
1575 int textlen;
1576 SmsAddressRec sender;
1577 SmsPDU* pdus;
1578 int nn;
1579
1580 /* check that we have a phone number made of digits */
1581 if (!args) {
1582 MissingArgument:
1583 control_write( client, "KO: missing argument, try 'sms send <phonenumber> <text message>'\r\n" );
1584 return -1;
1585 }
1586 p = strchr( args, ' ' );
1587 if (!p) {
1588 goto MissingArgument;
1589 }
1590
1591 if ( sms_address_from_str( &sender, args, p - args ) < 0 ) {
1592 control_write( client, "KO: bad phone number format, must be [+](0-9)*\r\n" );
1593 return -1;
1594 }
1595
1596
1597 /* un-secape message text into proper utf-8 (conversion happens in-site) */
1598 p += 1;
1599 textlen = strlen(p);
1600 textlen = sms_utf8_from_message_str( p, textlen, (unsigned char*)p, textlen );
1601 if (textlen < 0) {
1602 control_write( client, "message must be utf8 and can use the following escapes:\r\n"
1603 " \\n for a newline\r\n"
1604 " \\xNN where NN are two hexadecimal numbers\r\n"
1605 " \\uNNNN where NNNN are four hexadecimal numbers\r\n"
1606 " \\\\ to send a '\\' character\r\n\r\n"
1607 " anything else is an error\r\n"
1608 "KO: badly formatted text\r\n" );
1609 return -1;
1610 }
1611
1612 if (!android_modem) {
1613 control_write( client, "KO: modem emulation not running\r\n" );
1614 return -1;
1615 }
1616
1617 /* create a list of SMS PDUs, then send them */
1618 pdus = smspdu_create_deliver_utf8( (cbytes_t)p, textlen, &sender, NULL );
1619 if (pdus == NULL) {
1620 control_write( client, "KO: internal error when creating SMS-DELIVER PDUs\n" );
1621 return -1;
1622 }
1623
1624 for (nn = 0; pdus[nn] != NULL; nn++)
1625 amodem_receive_sms( android_modem, pdus[nn] );
1626
1627 smspdu_free_list( pdus );
1628 return 0;
1629}
1630
1631static int
1632do_sms_sendpdu( ControlClient client, char* args )
1633{
1634 SmsPDU pdu;
1635
1636 /* check that we have a phone number made of digits */
1637 if (!args) {
1638 control_write( client, "KO: missing argument, try 'sms sendpdu <hexstring>'\r\n" );
1639 return -1;
1640 }
1641
1642 if (!android_modem) {
1643 control_write( client, "KO: modem emulation not running\r\n" );
1644 return -1;
1645 }
1646
1647 pdu = smspdu_create_from_hex( args, strlen(args) );
1648 if (pdu == NULL) {
1649 control_write( client, "KO: badly formatted <hexstring>\r\n" );
1650 return -1;
1651 }
1652
1653 amodem_receive_sms( android_modem, pdu );
1654 smspdu_free( pdu );
1655 return 0;
1656}
1657
1658static const CommandDefRec sms_commands[] =
1659{
1660 { "send", "send inbound SMS text message",
1661 "'sms send <phonenumber> <message>' allows you to simulate a new inbound sms message\r\n", NULL,
1662 do_sms_send, NULL },
1663
1664 { "pdu", "send inbound SMS PDU",
1665 "'sms pdu <hexstring>' allows you to simulate a new inbound sms PDU\r\n"
1666 "(used internally when one emulator sends SMS messages to another instance).\r\n"
1667 "you probably don't want to play with this at all\r\n", NULL,
1668 do_sms_sendpdu, NULL },
1669
1670 { NULL, NULL, NULL, NULL, NULL, NULL }
1671};
1672
1673static void
1674do_control_write(void* data, const char* string)
1675{
1676 control_write((ControlClient)data, string);
1677}
1678
1679static int
1680do_power_display( ControlClient client, char* args )
1681{
1682 goldfish_battery_display(do_control_write, client);
1683 return 0;
1684}
1685
1686static int
1687do_ac_state( ControlClient client, char* args )
1688{
1689 if (args) {
1690 if (strcasecmp(args, "on") == 0) {
1691 goldfish_battery_set_prop(1, POWER_SUPPLY_PROP_ONLINE, 1);
1692 return 0;
1693 }
1694 if (strcasecmp(args, "off") == 0) {
1695 goldfish_battery_set_prop(1, POWER_SUPPLY_PROP_ONLINE, 0);
1696 return 0;
1697 }
1698 }
1699
1700 control_write( client, "KO: Usage: \"ac on\" or \"ac off\"\n" );
1701 return -1;
1702}
1703
1704static int
1705do_battery_status( ControlClient client, char* args )
1706{
1707 if (args) {
1708 if (strcasecmp(args, "unknown") == 0) {
1709 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_UNKNOWN);
1710 return 0;
1711 }
1712 if (strcasecmp(args, "charging") == 0) {
1713 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_CHARGING);
1714 return 0;
1715 }
1716 if (strcasecmp(args, "discharging") == 0) {
1717 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_DISCHARGING);
1718 return 0;
1719 }
1720 if (strcasecmp(args, "not-charging") == 0) {
1721 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_NOT_CHARGING);
1722 return 0;
1723 }
1724 if (strcasecmp(args, "full") == 0) {
1725 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_FULL);
1726 return 0;
1727 }
1728 }
1729
1730 control_write( client, "KO: Usage: \"status unknown|charging|discharging|not-charging|full\"\n" );
1731 return -1;
1732}
1733
1734static int
1735do_battery_present( ControlClient client, char* args )
1736{
1737 if (args) {
1738 if (strcasecmp(args, "true") == 0) {
1739 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_PRESENT, 1);
1740 return 0;
1741 }
1742 if (strcasecmp(args, "false") == 0) {
1743 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_PRESENT, 0);
1744 return 0;
1745 }
1746 }
1747
1748 control_write( client, "KO: Usage: \"present true\" or \"present false\"\n" );
1749 return -1;
1750}
1751
1752static int
1753do_battery_health( ControlClient client, char* args )
1754{
1755 if (args) {
1756 if (strcasecmp(args, "unknown") == 0) {
1757 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_UNKNOWN);
1758 return 0;
1759 }
1760 if (strcasecmp(args, "good") == 0) {
1761 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_GOOD);
1762 return 0;
1763 }
1764 if (strcasecmp(args, "overheat") == 0) {
1765 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_OVERHEAT);
1766 return 0;
1767 }
1768 if (strcasecmp(args, "dead") == 0) {
1769 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_DEAD);
1770 return 0;
1771 }
1772 if (strcasecmp(args, "overvoltage") == 0) {
1773 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_OVERVOLTAGE);
1774 return 0;
1775 }
1776 if (strcasecmp(args, "failure") == 0) {
1777 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_UNSPEC_FAILURE);
1778 return 0;
1779 }
1780 }
1781
1782 control_write( client, "KO: Usage: \"health unknown|good|overheat|dead|overvoltage|failure\"\n" );
1783 return -1;
1784}
1785
1786static int
1787do_battery_capacity( ControlClient client, char* args )
1788{
1789 if (args) {
1790 int capacity;
1791
1792 if (sscanf(args, "%d", &capacity) == 1 && capacity >= 0 && capacity <= 100) {
1793 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_CAPACITY, capacity);
1794 return 0;
1795 }
1796 }
1797
1798 control_write( client, "KO: Usage: \"capacity <percentage>\"\n" );
1799 return -1;
1800}
1801
1802
1803static const CommandDefRec power_commands[] =
1804{
1805 { "display", "display battery and charger state",
1806 "display battery and charger state\r\n", NULL,
1807 do_power_display, NULL },
1808
1809 { "ac", "set AC charging state",
1810 "'ac on|off' allows you to set the AC charging state to on or off\r\n", NULL,
1811 do_ac_state, NULL },
1812
1813 { "status", "set battery status",
1814 "'status unknown|charging|discharging|not-charging|full' allows you to set battery status\r\n", NULL,
1815 do_battery_status, NULL },
1816
1817 { "present", "set battery present state",
1818 "'present true|false' allows you to set battery present state to true or false\r\n", NULL,
1819 do_battery_present, NULL },
1820
1821 { "health", "set battery health state",
1822 "'health unknown|good|overheat|dead|overvoltage|failure' allows you to set battery health state\r\n", NULL,
1823 do_battery_health, NULL },
1824
1825 { "capacity", "set battery capacity state",
1826 "'capacity <percentage>' allows you to set battery capacity to a value 0 - 100\r\n", NULL,
1827 do_battery_capacity, NULL },
1828
1829 { NULL, NULL, NULL, NULL, NULL, NULL }
1830};
1831
1832/********************************************************************************************/
1833/********************************************************************************************/
1834/***** ******/
1835/***** E V E N T C O M M A N D S ******/
1836/***** ******/
1837/********************************************************************************************/
1838/********************************************************************************************/
1839
1840
1841static int
1842do_event_send( ControlClient client, char* args )
1843{
1844 char* p;
1845
1846 if (!args) {
1847 control_write( client, "KO: Usage: event send <type>:<code>:<value> ...\r\n" );
1848 return -1;
1849 }
1850
1851 p = args;
1852 while (*p) {
1853 char* q;
David 'Digit' Turner88935f72011-05-09 10:24:18 +02001854 char temp[128];
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001855 int type, code, value, ret;
1856
David 'Digit' Turner88935f72011-05-09 10:24:18 +02001857 p += strspn( p, " \t" ); /* skip spaces */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001858 if (*p == 0)
1859 break;
1860
1861 q = p + strcspn( p, " \t" );
1862
1863 if (q == p)
1864 break;
1865
Andrew Hsiehc7389bd2012-03-13 02:13:40 -07001866 snprintf(temp, sizeof temp, "%.*s", (int)(intptr_t)(q-p), p);
David 'Digit' Turner88935f72011-05-09 10:24:18 +02001867 ret = android_event_from_str( temp, &type, &code, &value );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001868 if (ret < 0) {
1869 if (ret == -1) {
1870 control_write( client,
1871 "KO: invalid event type in '%.*s', try 'event list types' for valid values\r\n",
1872 q-p, p );
1873 } else if (ret == -2) {
1874 control_write( client,
1875 "KO: invalid event code in '%.*s', try 'event list codes <type>' for valid values\r\n",
1876 q-p, p );
1877 } else {
1878 control_write( client,
1879 "KO: invalid event value in '%.*s', must be an integer\r\n",
1880 q-p, p);
1881 }
1882 return -1;
1883 }
1884
David 'Digit' Turner34f29742010-05-25 18:16:10 -07001885 user_event_generic( type, code, value );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001886 p = q;
1887 }
1888 return 0;
1889}
1890
1891static int
1892do_event_types( ControlClient client, char* args )
1893{
1894 int count = android_event_get_type_count();
1895 int nn;
1896
1897 control_write( client, "event <type> can be an integer or one of the following aliases\r\n" );
1898 for (nn = 0; nn < count; nn++) {
1899 char tmp[16];
1900 char* p = tmp;
1901 char* end = p + sizeof(tmp);
1902 int count2 = android_event_get_code_count( nn );;
1903
1904 p = android_event_bufprint_type_str( p, end, nn );
1905
1906 control_write( client, " %-8s", tmp );
1907 if (count2 > 0)
1908 control_write( client, " (%d code aliases)", count2 );
1909
1910 control_write( client, "\r\n" );
1911 }
1912 return 0;
1913}
1914
1915static int
1916do_event_codes( ControlClient client, char* args )
1917{
1918 int count;
1919 int nn, type, dummy;
1920
1921 if (!args) {
1922 control_write( client, "KO: argument missing, try 'event codes <type>'\r\n" );
1923 return -1;
1924 }
1925
1926 if ( android_event_from_str( args, &type, &dummy, &dummy ) < 0 ) {
1927 control_write( client, "KO: bad argument, see 'event types' for valid values\r\n" );
1928 return -1;
1929 }
1930
1931 count = android_event_get_code_count( type );
1932 if (count == 0) {
1933 control_write( client, "no code aliases defined for this type\r\n" );
1934 } else {
1935 control_write( client, "type '%s' accepts the following <code> aliases:\r\n",
1936 args );
1937 for (nn = 0; nn < count; nn++) {
1938 char temp[20], *p = temp, *end = p + sizeof(temp);
1939 android_event_bufprint_code_str( p, end, type, nn );
1940 control_write( client, " %-12s\r\n", temp );
1941 }
1942 }
1943
1944 return 0;
1945}
1946
1947static __inline__ int
1948utf8_next( unsigned char* *pp, unsigned char* end )
1949{
1950 unsigned char* p = *pp;
1951 int result = -1;
1952
1953 if (p < end) {
1954 int c= *p++;
1955 if (c >= 128) {
1956 if ((c & 0xe0) == 0xc0)
1957 c &= 0x1f;
1958 else if ((c & 0xf0) == 0xe0)
1959 c &= 0x0f;
1960 else
1961 c &= 0x07;
1962
1963 while (p < end && (p[0] & 0xc0) == 0x80) {
1964 c = (c << 6) | (p[0] & 0x3f);
1965 }
1966 }
1967 result = c;
1968 *pp = p;
1969 }
1970 return result;
1971}
1972
1973static int
1974do_event_text( ControlClient client, char* args )
1975{
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001976 AKeycodeBuffer keycodes;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001977 unsigned char* p = (unsigned char*) args;
1978 unsigned char* end = p + strlen(args);
1979 int textlen;
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001980 const AKeyCharmap* charmap;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001981
1982 if (!args) {
1983 control_write( client, "KO: argument missing, try 'event text <message>'\r\n" );
1984 return -1;
1985 }
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001986
David 'Digit' Turner0158ea32011-01-19 05:21:31 +01001987 /* Get active charmap. */
1988 charmap = android_get_charmap();
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001989 if (charmap == NULL) {
1990 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 -08001991 return -1;
1992 }
1993
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001994 keycodes.keycode_count = 0;
1995
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001996 /* un-secape message text into proper utf-8 (conversion happens in-site) */
1997 textlen = strlen((char*)p);
1998 textlen = sms_utf8_from_message_str( args, textlen, (unsigned char*)p, textlen );
1999 if (textlen < 0) {
2000 control_write( client, "message must be utf8 and can use the following escapes:\r\n"
2001 " \\n for a newline\r\n"
2002 " \\xNN where NN are two hexadecimal numbers\r\n"
2003 " \\uNNNN where NNNN are four hexadecimal numbers\r\n"
2004 " \\\\ to send a '\\' character\r\n\r\n"
2005 " anything else is an error\r\n"
2006 "KO: badly formatted text\r\n" );
2007 return -1;
2008 }
2009
2010 end = p + textlen;
2011 while (p < end) {
2012 int c = utf8_next( &p, end );
2013 if (c <= 0)
2014 break;
2015
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07002016 android_charmap_reverse_map_unicode( NULL, (unsigned)c, 1, &keycodes );
2017 android_charmap_reverse_map_unicode( NULL, (unsigned)c, 0, &keycodes );
2018 android_keycodes_flush( &keycodes );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002019 }
2020
2021 return 0;
2022}
2023
2024static const CommandDefRec event_commands[] =
2025{
2026 { "send", "send a series of events to the kernel",
2027 "'event send <type>:<code>:<value> ...' allows your to send one or more hardware events\r\n"
2028 "to the Android kernel. you can use text names or integers for <type> and <code>\r\n", NULL,
2029 do_event_send, NULL },
2030
2031 { "types", "list all <type> aliases",
2032 "'event types' list all <type> string aliases supported by the 'event' subcommands\r\n",
2033 NULL, do_event_types, NULL },
2034
2035 { "codes", "list all <code> aliases for a given <type>",
2036 "'event codes <type>' lists all <code> string aliases for a given event <type>\r\n",
2037 NULL, do_event_codes, NULL },
2038
2039 { "text", "simulate keystrokes from a given text",
2040 "'event text <message>' allows you to simulate keypresses to generate a given text\r\n"
2041 "message. <message> must be an utf-8 string. Unicode points will be reverse-mapped\r\n"
2042 "according to the current device keyboard. unsupported characters will be discarded\r\n"
2043 "silently\r\n", NULL, do_event_text, NULL },
2044
2045 { NULL, NULL, NULL, NULL, NULL, NULL }
2046};
2047
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002048
2049/********************************************************************************************/
2050/********************************************************************************************/
2051/***** ******/
2052/***** S N A P S H O T C O M M A N D S ******/
2053/***** ******/
2054/********************************************************************************************/
2055/********************************************************************************************/
2056
2057static int
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002058control_write_out_cb(void* opaque, const char* str, int strsize)
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002059{
2060 ControlClient client = opaque;
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002061 control_control_write(client, str, strsize);
2062 return strsize;
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002063}
2064
2065static int
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002066control_write_err_cb(void* opaque, const char* str, int strsize)
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002067{
2068 int ret = 0;
2069 ControlClient client = opaque;
2070 ret += control_write(client, "KO: ");
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002071 control_control_write(client, str, strsize);
2072 return ret + strsize;
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002073}
2074
2075static int
2076do_snapshot_list( ControlClient client, char* args )
2077{
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002078 int64_t ret;
2079 Monitor *out = monitor_fake_new(client, control_write_out_cb);
2080 Monitor *err = monitor_fake_new(client, control_write_err_cb);
2081 do_info_snapshots(out, err);
2082 ret = monitor_fake_get_bytes(err);
2083 monitor_fake_free(err);
2084 monitor_fake_free(out);
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002085
2086 return ret > 0;
2087}
2088
2089static int
2090do_snapshot_save( ControlClient client, char* args )
2091{
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002092 int64_t ret;
David 'Digit' Turner9fb360e2011-05-04 22:01:28 +02002093
2094 if (args == NULL) {
2095 control_write(client, "KO: argument missing, try 'avd snapshot save <name>'\r\n");
2096 return -1;
2097 }
2098
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002099 Monitor *err = monitor_fake_new(client, control_write_err_cb);
2100 do_savevm(err, args);
2101 ret = monitor_fake_get_bytes(err);
2102 monitor_fake_free(err);
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002103
2104 return ret > 0; // no output on error channel indicates success
2105}
2106
2107static int
2108do_snapshot_load( ControlClient client, char* args )
2109{
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002110 int64_t ret;
David 'Digit' Turner9fb360e2011-05-04 22:01:28 +02002111
2112 if (args == NULL) {
2113 control_write(client, "KO: argument missing, try 'avd snapshot load <name>'\r\n");
2114 return -1;
2115 }
2116
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002117 Monitor *err = monitor_fake_new(client, control_write_err_cb);
2118 do_loadvm(err, args);
2119 ret = monitor_fake_get_bytes(err);
2120 monitor_fake_free(err);
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002121
2122 return ret > 0;
2123}
2124
2125static int
2126do_snapshot_del( ControlClient client, char* args )
2127{
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002128 int64_t ret;
David 'Digit' Turner9fb360e2011-05-04 22:01:28 +02002129
2130 if (args == NULL) {
2131 control_write(client, "KO: argument missing, try 'avd snapshot del <name>'\r\n");
2132 return -1;
2133 }
2134
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002135 Monitor *err = monitor_fake_new(client, control_write_err_cb);
2136 do_delvm(err, args);
2137 ret = monitor_fake_get_bytes(err);
2138 monitor_fake_free(err);
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002139
2140 return ret > 0;
2141}
2142
2143static const CommandDefRec snapshot_commands[] =
2144{
2145 { "list", "list available state snapshots",
2146 "'avd snapshot list' will show a list of all state snapshots that can be loaded\r\n",
2147 NULL, do_snapshot_list, NULL },
2148
2149 { "save", "save state snapshot",
2150 "'avd snapshot save <name>' will save the current (run-time) state to a snapshot with the given name\r\n",
2151 NULL, do_snapshot_save, NULL },
2152
2153 { "load", "load state snapshot",
2154 "'avd snapshot load <name>' will load the state snapshot of the given name\r\n",
2155 NULL, do_snapshot_load, NULL },
2156
2157 { "del", "delete state snapshot",
2158 "'avd snapshot del <name>' will delete the state snapshot with the given name\r\n",
2159 NULL, do_snapshot_del, NULL },
2160
2161 { NULL, NULL, NULL, NULL, NULL, NULL }
2162};
2163
2164
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002165
2166/********************************************************************************************/
2167/********************************************************************************************/
2168/***** ******/
2169/***** V M C O M M A N D S ******/
2170/***** ******/
2171/********************************************************************************************/
2172/********************************************************************************************/
2173
2174static int
2175do_avd_stop( ControlClient client, char* args )
2176{
2177 if (!vm_running) {
2178 control_write( client, "KO: virtual device already stopped\r\n" );
2179 return -1;
2180 }
2181 vm_stop(EXCP_INTERRUPT);
2182 return 0;
2183}
2184
2185static int
2186do_avd_start( ControlClient client, char* args )
2187{
2188 if (vm_running) {
2189 control_write( client, "KO: virtual device already running\r\n" );
2190 return -1;
2191 }
2192 vm_start();
2193 return 0;
2194}
2195
2196static int
2197do_avd_status( ControlClient client, char* args )
2198{
2199 control_write( client, "virtual device is %s\r\n", vm_running ? "running" : "stopped" );
2200 return 0;
2201}
2202
2203static int
2204do_avd_name( ControlClient client, char* args )
2205{
David 'Digit' Turnerec6cedb2011-05-05 12:49:51 +02002206 control_write( client, "%s\r\n", android_hw->avd_name);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002207 return 0;
2208}
2209
2210static const CommandDefRec vm_commands[] =
2211{
2212 { "stop", "stop the virtual device",
2213 "'avd stop' stops the virtual device immediately, use 'avd start' to continue execution\r\n",
2214 NULL, do_avd_stop, NULL },
2215
2216 { "start", "start/restart the virtual device",
2217 "'avd start' will start or continue the virtual device, use 'avd stop' to stop it\r\n",
2218 NULL, do_avd_start, NULL },
2219
2220 { "status", "query virtual device status",
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002221 "'avd status' will indicate whether the virtual device is running or not\r\n",
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002222 NULL, do_avd_status, NULL },
2223
2224 { "name", "query virtual device name",
2225 "'avd name' will return the name of this virtual device\r\n",
2226 NULL, do_avd_name, NULL },
2227
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002228 { "snapshot", "state snapshot commands",
2229 "allows you to save and restore the virtual device state in snapshots\r\n",
2230 NULL, NULL, snapshot_commands },
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002231
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002232 { NULL, NULL, NULL, NULL, NULL, NULL }
2233};
2234
2235/********************************************************************************************/
2236/********************************************************************************************/
2237/***** ******/
2238/***** G E O C O M M A N D S ******/
2239/***** ******/
2240/********************************************************************************************/
2241/********************************************************************************************/
2242
2243static int
2244do_geo_nmea( ControlClient client, char* args )
2245{
2246 if (!args) {
2247 control_write( client, "KO: NMEA sentence missing, try 'help geo nmea'\r\n" );
2248 return -1;
2249 }
2250 if (!android_gps_cs) {
2251 control_write( client, "KO: no GPS emulation in this virtual device\r\n" );
2252 return -1;
2253 }
2254 android_gps_send_nmea( args );
2255 return 0;
2256}
2257
2258static int
2259do_geo_fix( ControlClient client, char* args )
2260{
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002261 // GEO_SAT2 provides bug backwards compatibility.
2262 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 -08002263 char* p = args;
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002264 int top_param = -1;
2265 double params[ NUM_GEO_PARAMS ];
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002266 int n_satellites = 1;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002267
2268 static int last_time = 0;
2269 static double last_altitude = 0.;
2270
2271 if (!p)
2272 p = "";
2273
2274 /* tokenize */
2275 while (*p) {
2276 char* end;
2277 double val = strtod( p, &end );
2278
2279 if (end == p) {
2280 control_write( client, "KO: argument '%s' is not a number\n", p );
2281 return -1;
2282 }
2283
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002284 params[++top_param] = val;
2285 if (top_param + 1 == NUM_GEO_PARAMS)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002286 break;
2287
2288 p = end;
2289 while (*p && (p[0] == ' ' || p[0] == '\t'))
2290 p += 1;
2291 }
2292
2293 /* sanity check */
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002294 if (top_param < GEO_LAT) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002295 control_write( client, "KO: not enough arguments: see 'help geo fix' for details\r\n" );
2296 return -1;
2297 }
2298
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002299 /* check number of satellites, must be integer between 1 and 12 */
2300 if (top_param >= GEO_SAT) {
2301 int sat_index = (top_param >= GEO_SAT2) ? GEO_SAT2 : GEO_SAT;
2302 n_satellites = (int) params[sat_index];
2303 if (n_satellites != params[sat_index]
2304 || n_satellites < 1 || n_satellites > 12) {
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002305 control_write( client, "KO: invalid number of satellites. Must be an integer between 1 and 12\r\n");
2306 return -1;
2307 }
2308 }
2309
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002310 /* generate an NMEA sentence for this fix */
2311 {
2312 STRALLOC_DEFINE(s);
2313 double val;
2314 int deg, min;
2315 char hemi;
2316
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002317 /* format overview:
2318 * time of fix 123519 12:35:19 UTC
2319 * latitude 4807.038 48 degrees, 07.038 minutes
2320 * north/south N or S
2321 * longitude 01131.000 11 degrees, 31. minutes
2322 * east/west E or W
2323 * fix quality 1 standard GPS fix
2324 * satellites 1 to 12 number of satellites being tracked
2325 * HDOP <dontcare> horizontal dilution
2326 * altitude 546. altitude above sea-level
2327 * altitude units M to indicate meters
2328 * diff <dontcare> height of sea-level above ellipsoid
2329 * diff units M to indicate meters (should be <dontcare>)
2330 * dgps age <dontcare> time in seconds since last DGPS fix
2331 * dgps sid <dontcare> DGPS station id
2332 */
2333
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002334 /* first, the time */
2335 stralloc_add_format( s, "$GPGGA,%06d", last_time );
2336 last_time ++;
2337
2338 /* then the latitude */
2339 hemi = 'N';
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002340 val = params[GEO_LAT];
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002341 if (val < 0) {
2342 hemi = 'S';
2343 val = -val;
2344 }
2345 deg = (int) val;
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002346 val = 60*(val - deg);
2347 min = (int) val;
David 'Digit' Turner631f2552010-10-27 02:46:53 +02002348 val = 10000*(val - min);
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002349 stralloc_add_format( s, ",%02d%02d.%04d,%c", deg, min, (int)val, hemi );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002350
2351 /* the longitude */
2352 hemi = 'E';
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002353 val = params[GEO_LONG];
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002354 if (val < 0) {
2355 hemi = 'W';
2356 val = -val;
2357 }
2358 deg = (int) val;
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002359 val = 60*(val - deg);
2360 min = (int) val;
David 'Digit' Turner631f2552010-10-27 02:46:53 +02002361 val = 10000*(val - min);
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002362 stralloc_add_format( s, ",%02d%02d.%04d,%c", deg, min, (int)val, hemi );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002363
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002364 /* bogus fix quality, satellite count and dilution */
2365 stralloc_add_format( s, ",1,%02d,", n_satellites );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002366
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002367 /* optional altitude + bogus diff */
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002368 if (top_param >= GEO_ALT) {
2369 stralloc_add_format( s, ",%.1g,M,0.,M", params[GEO_ALT] );
2370 last_altitude = params[GEO_ALT];
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002371 } else {
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002372 stralloc_add_str( s, ",,,," );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002373 }
2374 /* bogus rest and checksum */
2375 stralloc_add_str( s, ",,,*47" );
2376
2377 /* send it, then free */
2378 android_gps_send_nmea( stralloc_cstr(s) );
2379 stralloc_reset( s );
2380 }
2381 return 0;
2382}
2383
2384static const CommandDefRec geo_commands[] =
2385{
2386 { "nmea", "send an GPS NMEA sentence",
2387 "'geo nema <sentence>' sends a NMEA 0183 sentence to the emulated device, as\r\n"
2388 "if it came from an emulated GPS modem. <sentence> must begin with '$GP'. only\r\n"
2389 "'$GPGGA' and '$GPRCM' sentences are supported at the moment.\r\n",
2390 NULL, do_geo_nmea, NULL },
2391
2392 { "fix", "send a simple GPS fix",
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002393 "'geo fix <longitude> <latitude> [<altitude> [<satellites>]]'\r\n"
2394 " allows you to send a simple GPS fix to the emulated system.\r\n"
2395 " The parameters are:\r\n\r\n"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002396 " <longitude> longitude, in decimal degrees\r\n"
2397 " <latitude> latitude, in decimal degrees\r\n"
2398 " <altitude> optional altitude in meters\r\n"
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002399 " <satellites> number of satellites being tracked (1-12)\r\n"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002400 "\r\n",
2401 NULL, do_geo_fix, NULL },
2402
2403 { NULL, NULL, NULL, NULL, NULL, NULL }
2404};
2405
2406
2407/********************************************************************************************/
2408/********************************************************************************************/
2409/***** ******/
Tim Wan736e01f2011-01-10 10:58:25 +01002410/***** S E N S O R S C O M M A N D S ******/
2411/***** ******/
2412/********************************************************************************************/
2413/********************************************************************************************/
2414
2415/* For sensors user prompt string size.*/
2416#define SENSORS_INFO_SIZE 150
2417
2418/* Get sensor data - (a,b,c) from sensor name */
2419static int
2420do_sensors_get( ControlClient client, char* args )
2421{
2422 if (! args) {
2423 control_write( client, "KO: Usage: \"get <sensorname>\"\n" );
2424 return -1;
2425 }
2426
2427 int status = SENSOR_STATUS_UNKNOWN;
2428 char sensor[strlen(args) + 1];
2429 if (1 != sscanf( args, "%s", &sensor[0] ))
2430 goto SENSOR_STATUS_ERROR;
2431
2432 int sensor_id = android_sensors_get_id_from_name( sensor );
2433 char buffer[SENSORS_INFO_SIZE] = { 0 };
2434 float a, b, c;
2435
2436 if (sensor_id < 0) {
2437 status = sensor_id;
2438 goto SENSOR_STATUS_ERROR;
2439 } else {
2440 status = android_sensors_get( sensor_id, &a, &b, &c );
2441 if (status != SENSOR_STATUS_OK)
2442 goto SENSOR_STATUS_ERROR;
2443 snprintf( buffer, sizeof(buffer),
2444 "%s = %g:%g:%g\r\n", sensor, a, b, c );
2445 do_control_write( client, buffer );
2446 return 0;
2447 }
2448
2449SENSOR_STATUS_ERROR:
2450 switch(status) {
2451 case SENSOR_STATUS_NO_SERVICE:
2452 snprintf( buffer, sizeof(buffer), "KO: No sensor service found!\r\n" );
2453 break;
2454 case SENSOR_STATUS_DISABLED:
2455 snprintf( buffer, sizeof(buffer), "KO: '%s' sensor is disabled.\r\n", sensor );
2456 break;
2457 case SENSOR_STATUS_UNKNOWN:
2458 snprintf( buffer, sizeof(buffer),
2459 "KO: unknown sensor name: %s, run 'sensor status' to get available sensors.\r\n", sensor );
2460 break;
2461 default:
2462 snprintf( buffer, sizeof(buffer), "KO: '%s' sensor: exception happens.\r\n", sensor );
2463 }
2464 do_control_write( client, buffer );
2465 return -1;
2466}
2467
2468/* set sensor data - (a,b,c) from sensor name */
2469static int
2470do_sensors_set( ControlClient client, char* args )
2471{
2472 if (! args) {
2473 control_write( client, "KO: Usage: \"set <sensorname> <value-a>[:<value-b>[:<value-c>]]\"\n" );
2474 return -1;
2475 }
2476
2477 int status;
2478 char* sensor;
2479 char* value;
2480 char* args_dup = strdup( args );
2481 if (args_dup == NULL) {
2482 control_write( client, "KO: Memory allocation failed.\n" );
2483 return -1;
2484 }
2485 char* p = args_dup;
2486
2487 /* Parsing the args to get sensor name string */
2488 while (*p && isspace(*p)) p++;
2489 if (*p == 0)
2490 goto INPUT_ERROR;
2491 sensor = p;
2492
2493 /* Parsing the args to get value string */
2494 while (*p && (! isspace(*p))) p++;
2495 if (*p == 0 || *(p + 1) == 0/* make sure value isn't NULL */)
2496 goto INPUT_ERROR;
2497 *p = 0;
2498 value = p + 1;
2499
2500 if (! (strlen(sensor) && strlen(value)))
2501 goto INPUT_ERROR;
2502
2503 int sensor_id = android_sensors_get_id_from_name( sensor );
2504 char buffer[SENSORS_INFO_SIZE] = { 0 };
2505
2506 if (sensor_id < 0) {
2507 status = sensor_id;
2508 goto SENSOR_STATUS_ERROR;
2509 } else {
2510 float fvalues[3];
2511 status = android_sensors_get( sensor_id, &fvalues[0], &fvalues[1], &fvalues[2] );
2512 if (status != SENSOR_STATUS_OK)
2513 goto SENSOR_STATUS_ERROR;
2514
2515 /* Parsing the value part to get the sensor values(a, b, c) */
2516 int i;
2517 char* pnext;
2518 char* pend = value + strlen(value);
2519 for (i = 0; i < 3; i++, value = pnext + 1) {
2520 pnext=strchr( value, ':' );
2521 if (pnext) {
2522 *pnext = 0;
2523 } else {
2524 pnext = pend;
2525 }
2526
2527 if (pnext > value) {
2528 if (1 != sscanf( value,"%g", &fvalues[i] ))
2529 goto INPUT_ERROR;
2530 }
2531 }
2532
2533 status = android_sensors_set( sensor_id, fvalues[0], fvalues[1], fvalues[2] );
2534 if (status != SENSOR_STATUS_OK)
2535 goto SENSOR_STATUS_ERROR;
2536
2537 free( args_dup );
2538 return 0;
2539 }
2540
2541SENSOR_STATUS_ERROR:
2542 switch(status) {
2543 case SENSOR_STATUS_NO_SERVICE:
2544 snprintf( buffer, sizeof(buffer), "KO: No sensor service found!\r\n" );
2545 break;
2546 case SENSOR_STATUS_DISABLED:
2547 snprintf( buffer, sizeof(buffer), "KO: '%s' sensor is disabled.\r\n", sensor );
2548 break;
2549 case SENSOR_STATUS_UNKNOWN:
2550 snprintf( buffer, sizeof(buffer),
2551 "KO: unknown sensor name: %s, run 'sensor status' to get available sensors.\r\n", sensor );
2552 break;
2553 default:
2554 snprintf( buffer, sizeof(buffer), "KO: '%s' sensor: exception happens.\r\n", sensor );
2555 }
2556 do_control_write( client, buffer );
2557 free( args_dup );
2558 return -1;
2559
2560INPUT_ERROR:
2561 control_write( client, "KO: Usage: \"set <sensorname> <value-a>[:<value-b>[:<value-c>]]\"\n" );
2562 free( args_dup );
2563 return -1;
2564}
2565
2566/* get all available sensor names and enable status respectively. */
2567static int
2568do_sensors_status( ControlClient client, char* args )
2569{
2570 uint8_t id, status;
2571 char buffer[SENSORS_INFO_SIZE] = { 0 };
2572
2573 for(id = 0; id < MAX_SENSORS; id++) {
2574 status = android_sensors_get_sensor_status( id );
2575 snprintf( buffer, sizeof(buffer), "%s: %s\n",
2576 android_sensors_get_name_from_id(id), (status ? "enabled.":"disabled.") );
2577 control_write( client, buffer );
2578 }
2579
2580 return 0;
2581}
2582
2583/* Sensor commands for get/set sensor values and get available sensor names. */
2584static const CommandDefRec sensor_commands[] =
2585{
2586 { "status", "list all sensors and their status.",
2587 "'status': list all sensors and their status.\r\n",
2588 NULL, do_sensors_status, NULL },
2589
2590 { "get", "get sensor values",
2591 "'get <sensorname>' returns the values of a given sensor.\r\n",
2592 NULL, do_sensors_get, NULL },
2593
2594 { "set", "set sensor values",
2595 "'set <sensorname> <value-a>[:<value-b>[:<value-c>]]' set the values of a given sensor.\r\n",
2596 NULL, do_sensors_set, NULL },
2597
2598 { NULL, NULL, NULL, NULL, NULL, NULL }
2599};
2600
2601/********************************************************************************************/
2602/********************************************************************************************/
2603/***** ******/
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002604/***** M A I N C O M M A N D S ******/
2605/***** ******/
2606/********************************************************************************************/
2607/********************************************************************************************/
2608
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002609static int
2610do_window_scale( ControlClient client, char* args )
2611{
2612 double scale;
2613 int is_dpi = 0;
2614 char* end;
2615
2616 if (!args) {
2617 control_write( client, "KO: argument missing, try 'window scale <scale>'\r\n" );
2618 return -1;
2619 }
2620
2621 scale = strtol( args, &end, 10 );
2622 if (end > args && !memcmp( end, "dpi", 4 )) {
2623 is_dpi = 1;
2624 }
2625 else {
2626 scale = strtod( args, &end );
2627 if (end == args || end[0]) {
2628 control_write( client, "KO: argument <scale> must be a real number, or an integer followed by 'dpi'\r\n" );
2629 return -1;
2630 }
2631 }
2632
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -08002633 uicmd_set_window_scale( scale, is_dpi );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002634 return 0;
2635}
2636
2637static const CommandDefRec window_commands[] =
2638{
2639 { "scale", "change the window scale",
2640 "'window scale <scale>' allows you to change the scale of the emulator window at runtime\r\n"
2641 "<scale> must be either a real number between 0.1 and 3.0, or an integer followed by\r\n"
2642 "the 'dpi' prefix (as in '120dpi')\r\n",
2643 NULL, do_window_scale, NULL },
2644
2645 { NULL, NULL, NULL, NULL, NULL, NULL }
2646};
2647
2648/********************************************************************************************/
2649/********************************************************************************************/
2650/***** ******/
David 'Digit' Turnere92bc562010-09-07 06:21:25 -07002651/***** Q E M U C O M M A N D S ******/
2652/***** ******/
2653/********************************************************************************************/
2654/********************************************************************************************/
2655
2656static int
2657do_qemu_monitor( ControlClient client, char* args )
2658{
2659 char socketname[32];
2660 int fd;
2661 CharDriverState* cs;
2662
2663 if (args != NULL) {
2664 control_write( client, "KO: no argument for 'qemu monitor'\r\n" );
2665 return -1;
2666 }
2667 /* Detach the client socket, and re-attach it to a monitor */
2668 fd = control_client_detach(client);
2669 snprintf(socketname, sizeof socketname, "tcp:socket=%d", fd);
2670 cs = qemu_chr_open("monitor", socketname, NULL);
2671 if (cs == NULL) {
2672 control_client_reattach(client, fd);
2673 control_write( client, "KO: internal error: could not detach from console !\r\n" );
2674 return -1;
2675 }
2676 monitor_init(cs, MONITOR_USE_READLINE|MONITOR_QUIT_DOESNT_EXIT);
2677 control_client_destroy(client);
2678 return 0;
2679}
2680
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002681#ifdef CONFIG_STANDALONE_CORE
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002682/* UI settings, passed to the core via -ui-settings command line parameter. */
2683extern char* android_op_ui_settings;
2684
2685static int
2686do_attach_ui( ControlClient client, char* args )
2687{
2688 // Make sure that there are no UI already attached to this console.
2689 if (attached_ui_client != NULL) {
2690 control_write( client, "KO: Another UI is attached to this core!\r\n" );
2691 control_client_destroy(client);
2692 return -1;
2693 }
2694
Vladimir Chtchetkine85276802011-01-31 15:18:45 -08002695 if (!attachUiProxy_create(client->sock)) {
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002696 char reply_buf[4096];
Vladimir Chtchetkine85276802011-01-31 15:18:45 -08002697 attached_ui_client = client;
2698 // Reply "OK" with the saved -ui-settings property.
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002699 snprintf(reply_buf, sizeof(reply_buf), "OK: %s\r\n", android_op_ui_settings);
2700 control_write( client, reply_buf);
2701 } else {
Vladimir Chtchetkine85276802011-01-31 15:18:45 -08002702 control_write( client, "KO\r\n" );
2703 control_client_destroy(client);
2704 return -1;
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002705 }
2706
2707 return 0;
2708}
2709
Vladimir Chtchetkine85276802011-01-31 15:18:45 -08002710void
2711destroy_attach_ui_client(void)
2712{
2713 if (attached_ui_client != NULL) {
2714 control_client_destroy(attached_ui_client);
2715 }
2716}
2717
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002718static int
2719do_create_framebuffer_service( ControlClient client, char* args )
2720{
Vladimir Chtchetkine94a2fba2011-01-31 10:49:06 -08002721 ProxyFramebuffer* core_fb;
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002722 const char* protocol = "-raw"; // Default framebuffer exchange protocol.
David 'Digit' Turner7a5ee572011-02-02 15:58:45 +01002723 char reply_buf[64];
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002724
2725 // Protocol type is defined by the arguments passed with the stream switch
2726 // command.
2727 if (args != NULL && *args != '\0') {
2728 size_t token_len;
2729 const char* param_end = strchr(args, ' ');
2730 if (param_end == NULL) {
2731 param_end = args + strlen(args);
2732 }
2733 token_len = param_end - args;
2734 protocol = args;
2735
2736 // Make sure that this is one of the supported protocols.
2737 if (strncmp(protocol, "-raw", token_len) &&
2738 strncmp(protocol, "-shared", token_len)) {
2739 derror("Invalid framebuffer parameter %s\n", protocol);
2740 control_write( client, "KO: Invalid parameter\r\n" );
2741 control_client_destroy(client);
2742 return -1;
2743 }
2744 }
2745
David 'Digit' Turner7a5ee572011-02-02 15:58:45 +01002746 core_fb = proxyFb_create(client->sock, protocol);
2747 if (core_fb == NULL) {
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002748 control_write( client, "KO\r\n" );
2749 control_client_destroy(client);
2750 return -1;
2751 }
2752
David 'Digit' Turner7a5ee572011-02-02 15:58:45 +01002753 // Reply "OK" with the framebuffer's bits per pixel
2754 snprintf(reply_buf, sizeof(reply_buf), "OK: -bitsperpixel=%d\r\n",
2755 proxyFb_get_bits_per_pixel(core_fb));
2756 control_write( client, reply_buf);
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002757 return 0;
2758}
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -08002759
2760static int
2761do_create_user_events_service( ControlClient client, char* args )
2762{
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002763 // Make sure that there are no user events client already existing.
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -08002764 if (user_events_client != NULL) {
2765 control_write( client, "KO: Another user events service is already existing!\r\n" );
2766 control_client_destroy(client);
2767 return -1;
2768 }
2769
Vladimir Chtchetkine250b2e02011-01-28 10:56:16 -08002770 if (!userEventsImpl_create(client->sock)) {
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -08002771 char reply_buf[4096];
2772 user_events_client = client;
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -08002773 snprintf(reply_buf, sizeof(reply_buf), "OK\r\n");
2774 control_write( client, reply_buf);
2775 } else {
2776 control_write( client, "KO\r\n" );
2777 control_client_destroy(client);
2778 return -1;
2779 }
2780
2781 return 0;
2782}
Vladimir Chtchetkine6ee1c4e2011-01-20 11:22:32 -08002783
2784void
Vladimir Chtchetkine250b2e02011-01-28 10:56:16 -08002785destroy_user_events_client(void)
Vladimir Chtchetkine6ee1c4e2011-01-20 11:22:32 -08002786{
2787 if (user_events_client != NULL) {
2788 control_client_destroy(user_events_client);
2789 }
2790}
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002791
2792static int
2793do_create_ui_core_ctl_service( ControlClient client, char* args )
2794{
2795 // Make sure that there are no ui control client already existing.
2796 if (ui_core_ctl_client != NULL) {
2797 control_write( client, "KO: Another UI control service is already existing!\r\n" );
2798 control_client_destroy(client);
2799 return -1;
2800 }
2801
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -08002802 if (!coreCmdImpl_create(client->sock)) {
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002803 char reply_buf[4096];
2804 ui_core_ctl_client = client;
2805 snprintf(reply_buf, sizeof(reply_buf), "OK\r\n");
2806 control_write( client, reply_buf);
2807 } else {
2808 control_write( client, "KO\r\n" );
2809 control_client_destroy(client);
2810 return -1;
2811 }
2812
2813 return 0;
2814}
2815
2816void
2817destroy_ui_core_ctl_client(void)
2818{
2819 if (ui_core_ctl_client != NULL) {
2820 control_client_destroy(ui_core_ctl_client);
2821 }
2822}
2823
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -08002824void
2825destroy_corecmd_client(void)
2826{
2827 if (ui_core_ctl_client != NULL) {
2828 control_client_destroy(ui_core_ctl_client);
2829 }
2830}
2831
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002832static int
2833do_create_core_ui_ctl_service( ControlClient client, char* args )
2834{
2835 // Make sure that there are no ui control client already existing.
2836 if (core_ui_ctl_client != NULL) {
2837 control_write( client, "KO: Another UI control service is already existing!\r\n" );
2838 control_client_destroy(client);
2839 return -1;
2840 }
2841
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -08002842 if (!uiCmdProxy_create(client->sock)) {
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002843 char reply_buf[4096];
2844 core_ui_ctl_client = client;
2845 snprintf(reply_buf, sizeof(reply_buf), "OK\r\n");
2846 control_write( client, reply_buf);
2847 } else {
2848 control_write( client, "KO\r\n" );
2849 control_client_destroy(client);
2850 return -1;
2851 }
2852
2853 return 0;
2854}
2855
2856void
2857destroy_core_ui_ctl_client(void)
2858{
2859 if (core_ui_ctl_client != NULL) {
2860 control_client_destroy(core_ui_ctl_client);
2861 }
2862}
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -08002863
2864void
2865destroy_uicmd_client(void)
2866{
2867 if (core_ui_ctl_client != NULL) {
2868 control_client_destroy(core_ui_ctl_client);
2869 }
2870}
2871
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002872#endif // CONFIG_STANDALONE_CORE
2873
David 'Digit' Turnere92bc562010-09-07 06:21:25 -07002874static const CommandDefRec qemu_commands[] =
2875{
2876 { "monitor", "enter QEMU monitor",
2877 "Enter the QEMU virtual machine monitor\r\n",
2878 NULL, do_qemu_monitor, NULL },
2879
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002880#ifdef CONFIG_STANDALONE_CORE
Vladimir Chtchetkinea473d812011-01-26 08:53:05 -08002881 { "attach-UI", "attach UI to the core",
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002882 "Attach UI to the core\r\n",
2883 NULL, do_attach_ui, NULL },
2884
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002885 { "framebuffer", "create framebuffer service",
2886 "Create framebuffer service\r\n",
2887 NULL, do_create_framebuffer_service, NULL },
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -08002888
Vladimir Chtchetkinea473d812011-01-26 08:53:05 -08002889 { "user-events", "create user events service",
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -08002890 "Create user events service\r\n",
2891 NULL, do_create_user_events_service, NULL },
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002892
Vladimir Chtchetkinea473d812011-01-26 08:53:05 -08002893 { "ui-core-control", "create UI control service",
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002894 "Create UI control service\r\n",
2895 NULL, do_create_ui_core_ctl_service, NULL },
2896
Vladimir Chtchetkinea473d812011-01-26 08:53:05 -08002897 { "core-ui-control", "create UI control service",
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002898 "Create UI control service\r\n",
2899 NULL, do_create_core_ui_ctl_service, NULL },
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002900#endif // CONFIG_STANDALONE_CORE
2901
David 'Digit' Turnere92bc562010-09-07 06:21:25 -07002902 { NULL, NULL, NULL, NULL, NULL, NULL }
2903};
2904
2905
2906/********************************************************************************************/
2907/********************************************************************************************/
2908/***** ******/
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002909/***** M A I N C O M M A N D S ******/
2910/***** ******/
2911/********************************************************************************************/
2912/********************************************************************************************/
2913
2914static int
2915do_kill( ControlClient client, char* args )
2916{
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002917 control_write( client, "OK: killing emulator, bye bye\r\n" );
2918 exit(0);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002919}
2920
2921static const CommandDefRec main_commands[] =
2922{
2923 { "help|h|?", "print a list of commands", NULL, NULL, do_help, NULL },
2924
2925 { "event", "simulate hardware events",
2926 "allows you to send fake hardware events to the kernel\r\n", NULL,
2927 NULL, event_commands },
2928
2929 { "geo", "Geo-location commands",
2930 "allows you to change Geo-related settings, or to send GPS NMEA sentences\r\n", NULL,
2931 NULL, geo_commands },
2932
2933 { "gsm", "GSM related commands",
2934 "allows you to change GSM-related settings, or to make a new inbound phone call\r\n", NULL,
2935 NULL, gsm_commands },
2936
Jaime Lopez1a000852010-07-21 18:03:58 -07002937 { "cdma", "CDMA related commands",
2938 "allows you to change CDMA-related settings\r\n", NULL,
2939 NULL, cdma_commands },
2940
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002941 { "kill", "kill the emulator instance", NULL, NULL,
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002942 do_kill, NULL },
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002943
2944 { "network", "manage network settings",
2945 "allows you to manage the settings related to the network data connection of the\r\n"
2946 "emulated device.\r\n", NULL,
2947 NULL, network_commands },
2948
2949 { "power", "power related commands",
2950 "allows to change battery and AC power status\r\n", NULL,
2951 NULL, power_commands },
2952
2953 { "quit|exit", "quit control session", NULL, NULL,
2954 do_quit, NULL },
2955
2956 { "redir", "manage port redirections",
2957 "allows you to add, list and remove UDP and/or PORT redirection from the host to the device\r\n"
2958 "as an example, 'redir tcp:5000:6000' will route any packet sent to the host's TCP port 5000\r\n"
2959 "to TCP port 6000 of the emulated device\r\n", NULL,
2960 NULL, redir_commands },
2961
2962 { "sms", "SMS related commands",
2963 "allows you to simulate an inbound SMS\r\n", NULL,
2964 NULL, sms_commands },
2965
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002966 { "avd", "control virtual device execution",
2967 "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 -08002968 NULL, vm_commands },
2969
2970 { "window", "manage emulator window",
2971 "allows you to modify the emulator window\r\n", NULL,
2972 NULL, window_commands },
2973
David 'Digit' Turnere92bc562010-09-07 06:21:25 -07002974 { "qemu", "QEMU-specific commands",
2975 "allows to connect to the QEMU virtual machine monitor\r\n", NULL,
2976 NULL, qemu_commands },
2977
Tim Wan736e01f2011-01-10 10:58:25 +01002978 { "sensor", "manage emulator sensors",
2979 "allows you to request the emulator sensors\r\n", NULL,
2980 NULL, sensor_commands },
2981
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002982 { NULL, NULL, NULL, NULL, NULL, NULL }
2983};
2984
2985
2986static ControlGlobalRec _g_global;
2987
2988int
2989control_console_start( int port )
2990{
2991 return control_global_init( &_g_global, port );
2992}