blob: b033c1d372b5b976b7ba4b1f2c5e4e3fd8824e18 [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"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080056
57#if defined(CONFIG_SLIRP)
58#include "libslirp.h"
59#endif
60
David 'Digit' Turner496168e2014-07-12 05:28:13 +020061extern void android_emulator_set_window_scale(double, int);
62
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080063#define DEBUG 1
64
65#if 1
66# define D_ACTIVE VERBOSE_CHECK(console)
67#else
68# define D_ACTIVE DEBUG
69#endif
70
71#if DEBUG
72# define D(x) do { if (D_ACTIVE) ( printf x , fflush(stdout) ); } while (0)
73#else
74# define D(x) do{}while(0)
75#endif
76
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080077typedef struct ControlGlobalRec_* ControlGlobal;
78
79typedef struct ControlClientRec_* ControlClient;
80
81typedef struct {
82 int host_port;
83 int host_udp;
84 unsigned int guest_ip;
85 int guest_port;
86} RedirRec, *Redir;
87
88
David 'Digit' Turnere92bc562010-09-07 06:21:25 -070089typedef int Socket;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080090
91typedef struct ControlClientRec_
92{
93 struct ControlClientRec_* next; /* next client in list */
94 Socket sock; /* socket used for communication */
95 ControlGlobal global;
96 char finished;
97 char buff[ 4096 ];
98 int buff_len;
99
100} ControlClientRec;
101
102
103typedef struct ControlGlobalRec_
104{
105 /* listening socket */
106 Socket listen_fd;
107
108 /* the list of current clients */
109 ControlClient clients;
110
111 /* the list of redirections currently active */
112 Redir redirs;
113 int num_redirs;
114 int max_redirs;
115
116} ControlGlobalRec;
117
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800118#ifdef CONFIG_STANDALONE_CORE
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -0800119/* UI client currently attached to the core. */
120ControlClient attached_ui_client = NULL;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800121
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -0800122/* User events service client. */
123ControlClient user_events_client = NULL;
124
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -0800125/* UI control service client (UI -> Core). */
126ControlClient ui_core_ctl_client = NULL;
127
128/* UI control service (UI -> Core. */
129// CoreUICtl* ui_core_ctl = NULL;
130
131/* UI control service client (Core-> UI). */
132ControlClient core_ui_ctl_client = NULL;
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800133#endif // CONFIG_STANDALONE_CORE
134
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800135static int
136control_global_add_redir( ControlGlobal global,
137 int host_port,
138 int host_udp,
139 unsigned int guest_ip,
140 int guest_port )
141{
142 Redir redir;
143
144 if (global->num_redirs >= global->max_redirs)
145 {
146 int old_max = global->max_redirs;
147 int new_max = old_max + (old_max >> 1) + 4;
148
149 Redir new_redirs = realloc( global->redirs, new_max*sizeof(global->redirs[0]) );
150 if (new_redirs == NULL)
151 return -1;
152
153 global->redirs = new_redirs;
154 global->max_redirs = new_max;
155 }
156
157 redir = &global->redirs[ global->num_redirs++ ];
158
159 redir->host_port = host_port;
160 redir->host_udp = host_udp;
161 redir->guest_ip = guest_ip;
162 redir->guest_port = guest_port;
163
164 return 0;
165}
166
167static int
168control_global_del_redir( ControlGlobal global,
169 int host_port,
170 int host_udp )
171{
172 int nn;
173
174 for (nn = 0; nn < global->num_redirs; nn++)
175 {
176 Redir redir = &global->redirs[nn];
177
178 if ( redir->host_port == host_port &&
179 redir->host_udp == host_udp )
180 {
181 memmove( redir, redir + 1, ((global->num_redirs - nn)-1)*sizeof(*redir) );
182 global->num_redirs -= 1;
183 return 0;
184 }
185 }
186 /* we didn't find it */
187 return -1;
188}
189
David 'Digit' Turnere92bc562010-09-07 06:21:25 -0700190/* Detach the socket descriptor from a given ControlClient
191 * and return its value. This is useful either when destroying
192 * the client, or redirecting the socket to another service.
193 *
194 * NOTE: this does not close the socket.
195 */
196static int
197control_client_detach( ControlClient client )
198{
199 int result;
200
201 if (client->sock < 0)
202 return -1;
203
204 qemu_set_fd_handler( client->sock, NULL, NULL, NULL );
205 result = client->sock;
206 client->sock = -1;
207
208 return result;
209}
210
211static void control_client_read( void* _client ); /* forward */
212
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800213static void
214control_client_destroy( ControlClient client )
215{
216 ControlGlobal global = client->global;
217 ControlClient *pnode = &global->clients;
David 'Digit' Turnere92bc562010-09-07 06:21:25 -0700218 int sock;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800219
220 D(( "destroying control client %p\n", client ));
221
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800222#ifdef CONFIG_STANDALONE_CORE
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -0800223 if (client == attached_ui_client) {
Vladimir Chtchetkine85276802011-01-31 15:18:45 -0800224 attachUiProxy_destroy();
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -0800225 attached_ui_client = NULL;
226 }
227
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -0800228 if (client == user_events_client) {
Vladimir Chtchetkine250b2e02011-01-28 10:56:16 -0800229 userEventsImpl_destroy();
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -0800230 user_events_client = NULL;
231 }
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -0800232
233 if (client == ui_core_ctl_client) {
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800234 coreCmdImpl_destroy();
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -0800235 ui_core_ctl_client = NULL;
236 }
237
238 if (client == core_ui_ctl_client) {
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -0800239 uiCmdProxy_destroy();
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -0800240 core_ui_ctl_client = NULL;
241 }
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -0800242#endif // CONFIG_STANDALONE_CORE
243
David 'Digit' Turnere92bc562010-09-07 06:21:25 -0700244 sock = control_client_detach( client );
245 if (sock >= 0)
246 socket_close(sock);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800247
248 for ( ;; ) {
249 ControlClient node = *pnode;
250 if ( node == NULL )
251 break;
252 if ( node == client ) {
253 *pnode = node->next;
254 node->next = NULL;
255 break;
256 }
257 pnode = &node->next;
258 }
259
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800260 free( client );
261}
262
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800263
264
265static void control_control_write( ControlClient client, const char* buff, int len )
266{
267 int ret;
268
269 if (len < 0)
270 len = strlen(buff);
271
272 while (len > 0) {
David 'Digit' Turneraf81d742014-02-03 17:11:18 +0100273 ret = HANDLE_EINTR(socket_send( client->sock, buff, len));
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800274 if (ret < 0) {
David 'Digit' Turneraf81d742014-02-03 17:11:18 +0100275 if (errno != EWOULDBLOCK && errno != EAGAIN)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800276 return;
277 } else {
278 buff += ret;
279 len -= ret;
280 }
281 }
282}
283
Ot ten Thije2ff39a32010-10-06 17:48:15 +0100284static int control_vwrite( ControlClient client, const char* format, va_list args )
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800285{
286 static char temp[1024];
Ot ten Thije2ff39a32010-10-06 17:48:15 +0100287 int ret = vsnprintf( temp, sizeof(temp), format, args );
288 temp[ sizeof(temp)-1 ] = 0;
289 control_control_write( client, temp, -1 );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800290
Ot ten Thije2ff39a32010-10-06 17:48:15 +0100291 return ret;
292}
293
294static int control_write( ControlClient client, const char* format, ... )
295{
296 int ret;
297 va_list args;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800298 va_start(args, format);
Ot ten Thije2ff39a32010-10-06 17:48:15 +0100299 ret = control_vwrite(client, format, args);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800300 va_end(args);
301
Ot ten Thije2ff39a32010-10-06 17:48:15 +0100302 return ret;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800303}
304
305
306static ControlClient
307control_client_create( Socket socket,
308 ControlGlobal global )
309{
310 ControlClient client = calloc( sizeof(*client), 1 );
311
312 if (client) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800313 socket_set_nodelay( socket );
314 socket_set_nonblock( socket );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800315 client->finished = 0;
316 client->global = global;
317 client->sock = socket;
318 client->next = global->clients;
319 global->clients = client;
320
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800321 qemu_set_fd_handler( socket, control_client_read, NULL, client );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800322 }
323 return client;
324}
325
326typedef const struct CommandDefRec_ *CommandDef;
327
328typedef struct CommandDefRec_ {
329 const char* names;
330 const char* abstract;
331 const char* description;
332 void (*descriptor)( ControlClient client );
333 int (*handler)( ControlClient client, char* args );
334 CommandDef subcommands; /* if handler is NULL */
335
336} CommandDefRec;
337
338static const CommandDefRec main_commands[]; /* forward */
339
340static CommandDef
341find_command( char* input, CommandDef commands, char* *pend, char* *pargs )
342{
343 int nn;
344 char* args = strchr(input, ' ');
345
346 if (args != NULL) {
347 while (*args == ' ')
348 args++;
349
350 if (args[0] == 0)
351 args = NULL;
352 }
353
354 for (nn = 0; commands[nn].names != NULL; nn++)
355 {
356 const char* name = commands[nn].names;
357 const char* sep;
358
359 do {
360 int len, c;
361
362 sep = strchr( name, '|' );
363 if (sep)
364 len = sep - name;
365 else
366 len = strlen(name);
367
368 c = input[len];
369 if ( !memcmp( name, input, len ) && (c == ' ' || c == 0) ) {
370 *pend = input + len;
371 *pargs = args;
372 return &commands[nn];
373 }
374
375 if (sep)
376 name = sep + 1;
377
378 } while (sep != NULL && *name);
379 }
380 /* NOTE: don't touch *pend and *pargs if no command is found */
381 return NULL;
382}
383
384static void
385dump_help( ControlClient client,
386 CommandDef cmd,
387 const char* prefix )
388{
389 if (cmd->description) {
390 control_write( client, "%s", cmd->description );
391 } else if (cmd->descriptor) {
392 cmd->descriptor( client );
393 } else
394 control_write( client, "%s\r\n", cmd->abstract );
395
396 if (cmd->subcommands) {
397 cmd = cmd->subcommands;
398 control_write( client, "\r\navailable sub-commands:\r\n" );
399 for ( ; cmd->names != NULL; cmd++ ) {
400 control_write( client, " %s %-15s %s\r\n", prefix, cmd->names, cmd->abstract );
401 }
402 control_write( client, "\r\n" );
403 }
404}
405
406static void
407control_client_do_command( ControlClient client )
408{
409 char* line = client->buff;
410 char* args = NULL;
411 CommandDef commands = main_commands;
412 char* cmdend = client->buff;
413 CommandDef cmd = find_command( line, commands, &cmdend, &args );
414
415 if (cmd == NULL) {
416 control_write( client, "KO: unknown command, try 'help'\r\n" );
417 return;
418 }
419
420 for (;;) {
421 CommandDef subcmd;
422
423 if (cmd->handler) {
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -0800424 if ( !cmd->handler( client, args ) ) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800425 control_write( client, "OK\r\n" );
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -0800426 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800427 break;
428 }
429
430 /* no handler means we should have sub-commands */
431 if (cmd->subcommands == NULL) {
432 control_write( client, "KO: internal error: buggy command table for '%.*s'\r\n",
433 cmdend - client->buff, client->buff );
434 break;
435 }
436
437 /* we need a sub-command here */
438 if ( !args ) {
439 dump_help( client, cmd, "" );
440 control_write( client, "KO: missing sub-command\r\n" );
441 break;
442 }
443
444 line = args;
445 commands = cmd->subcommands;
446 subcmd = find_command( line, commands, &cmdend, &args );
447 if (subcmd == NULL) {
448 dump_help( client, cmd, "" );
449 control_write( client, "KO: bad sub-command\r\n" );
450 break;
451 }
452 cmd = subcmd;
453 }
454}
455
456/* implement the 'help' command */
457static int
458do_help( ControlClient client, char* args )
459{
460 char* line;
461 char* start = args;
462 char* end = start;
463 CommandDef cmd = main_commands;
464
465 /* without arguments, simply dump all commands */
466 if (args == NULL) {
467 control_write( client, "Android console command help:\r\n\r\n" );
468 for ( ; cmd->names != NULL; cmd++ ) {
469 control_write( client, " %-15s %s\r\n", cmd->names, cmd->abstract );
470 }
471 control_write( client, "\r\ntry 'help <command>' for command-specific help\r\n" );
472 return 0;
473 }
474
475 /* with an argument, find the corresponding command */
476 for (;;) {
477 CommandDef subcmd;
478
479 line = args;
480 subcmd = find_command( line, cmd, &end, &args );
481 if (subcmd == NULL) {
482 control_write( client, "try one of these instead:\r\n\r\n" );
483 for ( ; cmd->names != NULL; cmd++ ) {
484 control_write( client, " %.*s %s\r\n",
485 end - start, start, cmd->names );
486 }
487 control_write( client, "\r\nKO: unknown command\r\n" );
488 return -1;
489 }
490
491 if ( !args || !subcmd->subcommands ) {
492 dump_help( client, subcmd, start );
493 return 0;
494 }
495 cmd = subcmd->subcommands;
496 }
497}
498
499
500static void
501control_client_read_byte( ControlClient client, unsigned char ch )
502{
503 if (ch == '\r')
504 {
505 /* filter them out */
506 }
507 else if (ch == '\n')
508 {
509 client->buff[ client->buff_len ] = 0;
510 control_client_do_command( client );
511 if (client->finished)
512 return;
513
514 client->buff_len = 0;
515 }
516 else
517 {
518 if (client->buff_len >= sizeof(client->buff)-1)
519 client->buff_len = 0;
520
521 client->buff[ client->buff_len++ ] = ch;
522 }
523}
524
525static void
526control_client_read( void* _client )
527{
528 ControlClient client = _client;
529 unsigned char buf[4096];
530 int size;
531
532 D(( "in control_client read: " ));
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800533 size = socket_recv( client->sock, buf, sizeof(buf) );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800534 if (size < 0) {
535 D(( "size < 0, exiting with %d: %s\n", errno, errno_str ));
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -0800536 if (errno != EWOULDBLOCK && errno != EAGAIN)
537 control_client_destroy( client );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800538 return;
539 }
540
541 if (size == 0) {
542 /* end of connection */
543 D(( "end of connection detected !!\n" ));
544 control_client_destroy( client );
545 }
546 else {
547 int nn;
548#ifdef _WIN32
549# if DEBUG
550 char temp[16];
551 int count = size > sizeof(temp)-1 ? sizeof(temp)-1 : size;
552 for (nn = 0; nn < count; nn++) {
553 int c = buf[nn];
554 if (c == '\n')
555 temp[nn] = '!';
556 else if (c < 32)
557 temp[nn] = '.';
558 else
559 temp[nn] = (char)c;
560 }
561 temp[nn] = 0;
562 D(( "received %d bytes: %s\n", size, temp ));
563# endif
564#else
565 D(( "received %.*s\n", size, buf ));
566#endif
567 for (nn = 0; nn < size; nn++) {
568 control_client_read_byte( client, buf[nn] );
569 if (client->finished) {
570 control_client_destroy(client);
571 return;
572 }
573 }
574 }
575}
576
577
578/* this function is called on each new client connection */
579static void
580control_global_accept( void* _global )
581{
582 ControlGlobal global = _global;
583 ControlClient client;
584 Socket fd;
585
David 'Digit' Turner80bc5c82010-10-20 19:04:51 +0200586 D(( "control_global_accept: just in (fd=%d)\n", global->listen_fd ));
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800587
David 'Digit' Turneraf81d742014-02-03 17:11:18 +0100588 fd = HANDLE_EINTR(socket_accept(global->listen_fd, NULL));
589 if (fd < 0) {
590 D(( "problem in accept: %d: %s\n", errno, errno_str ));
591 perror("accept");
592 return;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800593 }
594
595 socket_set_xreuseaddr( fd );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800596
597 D(( "control_global_accept: creating new client\n" ));
598 client = control_client_create( fd, global );
599 if (client) {
600 D(( "control_global_accept: new client %p\n", client ));
601 control_write( client, "Android Console: type 'help' for a list of commands\r\n" );
602 control_write( client, "OK\r\n" );
603 }
604}
605
606
607static int
608control_global_init( ControlGlobal global,
609 int control_port )
610{
611 Socket fd;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800612 int ret;
613 SockAddress sockaddr;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800614
615 memset( global, 0, sizeof(*global) );
616
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800617 fd = socket_create_inet( SOCKET_STREAM );
618 if (fd < 0) {
619 perror("socket");
620 return -1;
621 }
622
623 socket_set_xreuseaddr( fd );
624
625 sock_address_init_inet( &sockaddr, SOCK_ADDRESS_INET_LOOPBACK, control_port );
626
627 ret = socket_bind(fd, &sockaddr );
628 if (ret < 0) {
629 perror("bind");
630 socket_close( fd );
631 return -1;
632 }
633
634 ret = socket_listen(fd, 0);
635 if (ret < 0) {
636 perror("listen");
637 socket_close( fd );
638 return -1;
639 }
640
641 socket_set_nonblock(fd);
642
643 global->listen_fd = fd;
644
645 qemu_set_fd_handler( fd, control_global_accept, NULL, global );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800646 return 0;
647}
648
649
650
651static int
652do_quit( ControlClient client, char* args )
653{
654 client->finished = 1;
655 return -1;
656}
657
658/********************************************************************************************/
659/********************************************************************************************/
660/***** ******/
661/***** N E T W O R K S E T T I N G S ******/
662/***** ******/
663/********************************************************************************************/
664/********************************************************************************************/
665
666static int
667do_network_status( ControlClient client, char* args )
668{
669 control_write( client, "Current network status:\r\n" );
670
671 control_write( client, " download speed: %8d bits/s (%.1f KB/s)\r\n",
672 (long)qemu_net_download_speed, qemu_net_download_speed/8192. );
673
674 control_write( client, " upload speed: %8d bits/s (%.1f KB/s)\r\n",
675 (long)qemu_net_upload_speed, qemu_net_upload_speed/8192. );
676
677 control_write( client, " minimum latency: %ld ms\r\n", qemu_net_min_latency );
678 control_write( client, " maximum latency: %ld ms\r\n", qemu_net_max_latency );
679 return 0;
680}
681
682static void
683dump_network_speeds( ControlClient client )
684{
685 const NetworkSpeed* speed = android_netspeeds;
686 const char* const format = " %-8s %s\r\n";
687 for ( ; speed->name; speed++ ) {
688 control_write( client, format, speed->name, speed->display );
689 }
690 control_write( client, format, "<num>", "selects both upload and download speed" );
691 control_write( client, format, "<up>:<down>", "select individual upload/download speeds" );
692}
693
694
695static int
696do_network_speed( ControlClient client, char* args )
697{
698 if ( !args ) {
699 control_write( client, "KO: missing <speed> argument, see 'help network speed'\r\n" );
700 return -1;
701 }
702 if ( android_parse_network_speed( args ) < 0 ) {
703 control_write( client, "KO: invalid <speed> argument, see 'help network speed' for valid values\r\n" );
704 return -1;
705 }
706
707 netshaper_set_rate( slirp_shaper_in, qemu_net_download_speed );
708 netshaper_set_rate( slirp_shaper_out, qemu_net_upload_speed );
709
710 if (android_modem) {
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -0700711 amodem_set_data_network_type( android_modem,
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800712 android_parse_network_type( args ) );
713 }
714 return 0;
715}
716
717static void
718describe_network_speed( ControlClient client )
719{
720 control_write( client,
721 "'network speed <speed>' allows you to dynamically change the speed of the emulated\r\n"
722 "network on the device, where <speed> is one of the following:\r\n\r\n" );
723 dump_network_speeds( client );
724}
725
726static int
727do_network_delay( ControlClient client, char* args )
728{
729 if ( !args ) {
730 control_write( client, "KO: missing <delay> argument, see 'help network delay'\r\n" );
731 return -1;
732 }
733 if ( android_parse_network_latency( args ) < 0 ) {
734 control_write( client, "KO: invalid <delay> argument, see 'help network delay' for valid values\r\n" );
735 return -1;
736 }
737 netdelay_set_latency( slirp_delay_in, qemu_net_min_latency, qemu_net_max_latency );
738 return 0;
739}
740
741static void
742describe_network_delay( ControlClient client )
743{
744 control_write( client,
745 "'network delay <latency>' allows you to dynamically change the latency of the emulated\r\n"
746 "network on the device, where <latency> is one of the following:\r\n\r\n" );
747 /* XXX: TODO */
748}
749
750static int
751do_network_capture_start( ControlClient client, char* args )
752{
753 if ( !args ) {
754 control_write( client, "KO: missing <file> argument, see 'help network capture start'\r\n" );
755 return -1;
756 }
757 if ( qemu_tcpdump_start(args) < 0) {
758 control_write( client, "KO: could not start capture: %s", strerror(errno) );
759 return -1;
760 }
761 return 0;
762}
763
764static int
765do_network_capture_stop( ControlClient client, char* args )
766{
767 /* no need to return an error here */
768 qemu_tcpdump_stop();
769 return 0;
770}
771
772static const CommandDefRec network_capture_commands[] =
773{
774 { "start", "start network capture",
775 "'network capture start <file>' starts a new capture of network packets\r\n"
776 "into a specific <file>. This will stop any capture already in progress.\r\n"
777 "the capture file can later be analyzed by tools like WireShark. It uses\r\n"
778 "the libpcap file format.\r\n\r\n"
779 "you can stop the capture anytime with 'network capture stop'\r\n", NULL,
780 do_network_capture_start, NULL },
781
782 { "stop", "stop network capture",
783 "'network capture stop' stops a currently running packet capture, if any.\r\n"
784 "you can start one with 'network capture start <file>'\r\n", NULL,
785 do_network_capture_stop, NULL },
786
787 { NULL, NULL, NULL, NULL, NULL, NULL }
788};
789
790static const CommandDefRec network_commands[] =
791{
792 { "status", "dump network status", NULL, NULL,
793 do_network_status, NULL },
794
795 { "speed", "change network speed", NULL, describe_network_speed,
796 do_network_speed, NULL },
797
798 { "delay", "change network latency", NULL, describe_network_delay,
799 do_network_delay, NULL },
800
801 { "capture", "dump network packets to file",
802 "allows to start/stop capture of network packets to a file for later analysis\r\n", NULL,
803 NULL, network_capture_commands },
804
805 { NULL, NULL, NULL, NULL, NULL, NULL }
806};
807
808/********************************************************************************************/
809/********************************************************************************************/
810/***** ******/
811/***** P O R T R E D I R E C T I O N S ******/
812/***** ******/
813/********************************************************************************************/
814/********************************************************************************************/
815
816static int
817do_redir_list( ControlClient client, char* args )
818{
819 ControlGlobal global = client->global;
820
821 if (global->num_redirs == 0)
822 control_write( client, "no active redirections\r\n" );
823 else {
824 int nn;
825 for (nn = 0; nn < global->num_redirs; nn++) {
826 Redir redir = &global->redirs[nn];
827 control_write( client, "%s:%-5d => %-5d\r\n",
828 redir->host_udp ? "udp" : "tcp",
829 redir->host_port,
830 redir->guest_port );
831 }
832 }
833 return 0;
834}
835
836/* parse a protocol:port specification */
837static int
838redir_parse_proto_port( char* args, int *pport, int *pproto )
839{
840 int proto = -1;
841 int len = 0;
842 char* end;
843
844 if ( !memcmp( args, "tcp:", 4 ) ) {
845 proto = 0;
846 len = 4;
847 }
848 else if ( !memcmp( args, "udp:", 4 ) ) {
849 proto = 1;
850 len = 4;
851 }
852 else
853 return 0;
854
855 args += len;
856 *pproto = proto;
857 *pport = strtol( args, &end, 10 );
858 if (end == args)
859 return 0;
860
861 len += end - args;
862 return len;
863}
864
865static int
866redir_parse_guest_port( char* arg, int *pport )
867{
868 char* end;
869
870 *pport = strtoul( arg, &end, 10 );
871 if (end == arg)
872 return 0;
873
874 return end - arg;
875}
876
877static Redir
878redir_find( ControlGlobal global, int port, int isudp )
879{
880 int nn;
881
882 for (nn = 0; nn < global->num_redirs; nn++) {
883 Redir redir = &global->redirs[nn];
884
885 if (redir->host_port == port && redir->host_udp == isudp)
886 return redir;
887 }
888 return NULL;
889}
890
891
892static int
893do_redir_add( ControlClient client, char* args )
894{
895 int len, host_proto, host_port, guest_port;
896 uint32_t guest_ip;
897 Redir redir;
898
899 if ( !args )
900 goto BadFormat;
901
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700902 if (!slirp_is_inited()) {
903 control_write( client, "KO: network emulation disabled\r\n");
904 return -1;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800905 }
906
907 len = redir_parse_proto_port( args, &host_port, &host_proto );
908 if (len == 0 || args[len] != ':')
909 goto BadFormat;
910
911 args += len + 1;
912 len = redir_parse_guest_port( args, &guest_port );
913 if (len == 0 || args[len] != 0)
914 goto BadFormat;
915
916 redir = redir_find( client->global, host_port, host_proto );
917 if ( redir != NULL ) {
918 control_write( client, "KO: host port already active, use 'redir del' to remove first\r\n" );
919 return -1;
920 }
921
David Turner7d9a2702009-04-14 14:43:24 -0700922 if (inet_strtoip("10.0.2.15", &guest_ip) < 0) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800923 control_write( client, "KO: unexpected internal failure when resolving 10.0.2.15\r\n" );
924 return -1;
925 }
926
927 D(("pattern hport=%d gport=%d proto=%d\n", host_port, guest_port, host_proto ));
928 if ( control_global_add_redir( client->global, host_port, host_proto,
929 guest_ip, guest_port ) < 0 )
930 {
931 control_write( client, "KO: not enough memory to allocate redirection\r\n" );
932 return -1;
933 }
934
935 if (slirp_redir(host_proto, host_port, guest_ip, guest_port) < 0) {
936 control_write( client, "KO: can't setup redirection, port probably used by another program on host\r\n" );
937 control_global_del_redir( client->global, host_port, host_proto );
938 return -1;
939 }
940
941 return 0;
942
943BadFormat:
944 control_write( client, "KO: bad redirection format, try (tcp|udp):hostport:guestport\r\n", -1 );
945 return -1;
946}
947
948
949static int
950do_redir_del( ControlClient client, char* args )
951{
952 int len, proto, port;
953 Redir redir;
954
955 if ( !args )
956 goto BadFormat;
957 len = redir_parse_proto_port( args, &port, &proto );
958 if ( len == 0 || args[len] != 0 )
959 goto BadFormat;
960
961 redir = redir_find( client->global, port, proto );
962 if (redir == NULL) {
963 control_write( client, "KO: can't remove unknown redirection (%s:%d)\r\n",
964 proto ? "udp" : "tcp", port );
965 return -1;
966 }
967
968 slirp_unredir( redir->host_udp, redir->host_port );
969 control_global_del_redir( client->global, port, proto );\
970
971 return 0;
972
973BadFormat:
974 control_write( client, "KO: bad redirection format, try (tcp|udp):hostport\r\n" );
975 return -1;
976}
977
978static const CommandDefRec redir_commands[] =
979{
980 { "list", "list current redirections",
981 "list current port redirections. use 'redir add' and 'redir del' to add and remove them\r\n", NULL,
982 do_redir_list, NULL },
983
984 { "add", "add new redirection",
985 "add a new port redirection, arguments must be:\r\n\r\n"
986 " redir add <protocol>:<host-port>:<guest-port>\r\n\r\n"
987 "where: <protocol> is either 'tcp' or 'udp'\r\n"
988 " <host-port> a number indicating which port on the host to open\r\n"
989 " <guest-port> a number indicating which port to route to on the device\r\n"
990 "\r\nas an example, 'redir tcp:5000:6000' will allow any packets sent to\r\n"
991 "the host's TCP port 5000 to be routed to TCP port 6000 of the emulated device\r\n", NULL,
992 do_redir_add, NULL },
993
994 { "del", "remove existing redirection",
995 "remove a port redirecion that was created with 'redir add', arguments must be:\r\n\r\n"
996 " redir del <protocol>:<host-port>\r\n\r\n"
997 "see the 'help redir add' for the meaning of <protocol> and <host-port>\r\n", NULL,
998 do_redir_del, NULL },
999
1000 { NULL, NULL, NULL, NULL, NULL, NULL }
1001};
1002
1003
1004
1005/********************************************************************************************/
1006/********************************************************************************************/
1007/***** ******/
Jaime Lopez1a000852010-07-21 18:03:58 -07001008/***** C D M A M O D E M ******/
1009/***** ******/
1010/********************************************************************************************/
1011/********************************************************************************************/
1012
1013static const struct {
1014 const char * name;
1015 const char * display;
1016 ACdmaSubscriptionSource source;
1017} _cdma_subscription_sources[] = {
1018 { "nv", "Read subscription from non-volatile RAM", A_SUBSCRIPTION_NVRAM },
1019 { "ruim", "Read subscription from RUIM", A_SUBSCRIPTION_RUIM },
1020};
1021
1022static void
1023dump_subscription_sources( ControlClient client )
1024{
1025 int i;
1026 for (i = 0;
1027 i < sizeof(_cdma_subscription_sources) / sizeof(_cdma_subscription_sources[0]);
1028 i++) {
1029 control_write( client, " %s: %s\r\n",
1030 _cdma_subscription_sources[i].name,
1031 _cdma_subscription_sources[i].display );
1032 }
1033}
1034
1035static void
1036describe_subscription_source( ControlClient client )
1037{
1038 control_write( client,
1039 "'cdma ssource <ssource>' allows you to specify where to read the subscription from\r\n" );
1040 dump_subscription_sources( client );
1041}
1042
1043static int
1044do_cdma_ssource( ControlClient client, char* args )
1045{
1046 int nn;
1047 if (!args) {
1048 control_write( client, "KO: missing argument, try 'cdma ssource <source>'\r\n" );
1049 return -1;
1050 }
1051
1052 for (nn = 0; ; nn++) {
1053 const char* name = _cdma_subscription_sources[nn].name;
1054 ACdmaSubscriptionSource ssource = _cdma_subscription_sources[nn].source;
1055
1056 if (!name)
1057 break;
1058
1059 if (!strcasecmp( args, name )) {
1060 amodem_set_cdma_subscription_source( android_modem, ssource );
1061 return 0;
1062 }
1063 }
1064 control_write( client, "KO: Don't know source %s\r\n", args );
1065 return -1;
1066}
1067
1068static int
1069do_cdma_prl_version( ControlClient client, char * args )
1070{
1071 int version = 0;
1072 char *endptr;
1073
1074 if (!args) {
1075 control_write( client, "KO: missing argument, try 'cdma prl_version <version>'\r\n");
1076 return -1;
1077 }
1078
1079 version = strtol(args, &endptr, 0);
1080 if (endptr != args) {
1081 amodem_set_cdma_prl_version( android_modem, version );
1082 }
David 'Digit' Turner80bc5c82010-10-20 19:04:51 +02001083 return 0;
Jaime Lopez1a000852010-07-21 18:03:58 -07001084}
1085/********************************************************************************************/
1086/********************************************************************************************/
1087/***** ******/
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001088/***** G S M M O D E M ******/
1089/***** ******/
1090/********************************************************************************************/
1091/********************************************************************************************/
1092
1093static const struct {
1094 const char* name;
1095 const char* display;
1096 ARegistrationState state;
1097} _gsm_states[] = {
1098 { "unregistered", "no network available", A_REGISTRATION_UNREGISTERED },
1099 { "home", "on local network, non-roaming", A_REGISTRATION_HOME },
1100 { "roaming", "on roaming network", A_REGISTRATION_ROAMING },
1101 { "searching", "searching networks", A_REGISTRATION_SEARCHING },
1102 { "denied", "emergency calls only", A_REGISTRATION_DENIED },
1103 { "off", "same as 'unregistered'", A_REGISTRATION_UNREGISTERED },
1104 { "on", "same as 'home'", A_REGISTRATION_HOME },
1105 { NULL, NULL, A_REGISTRATION_UNREGISTERED }
1106};
1107
1108static const char*
1109gsm_state_to_string( ARegistrationState state )
1110{
1111 int nn;
1112 for (nn = 0; _gsm_states[nn].name != NULL; nn++) {
1113 if (state == _gsm_states[nn].state)
1114 return _gsm_states[nn].name;
1115 }
1116 return "<unknown>";
1117}
1118
1119static int
1120do_gsm_status( ControlClient client, char* args )
1121{
1122 if (args) {
1123 control_write( client, "KO: no argument required\r\n" );
1124 return -1;
1125 }
1126 if (!android_modem) {
1127 control_write( client, "KO: modem emulation not running\r\n" );
1128 return -1;
1129 }
1130 control_write( client, "gsm voice state: %s\r\n",
1131 gsm_state_to_string(
1132 amodem_get_voice_registration(android_modem) ) );
1133 control_write( client, "gsm data state: %s\r\n",
1134 gsm_state_to_string(
1135 amodem_get_data_registration(android_modem) ) );
1136 return 0;
1137}
1138
1139
1140static void
1141help_gsm_data( ControlClient client )
1142{
1143 int nn;
1144 control_write( client,
1145 "the 'gsm data <state>' allows you to change the state of your GPRS connection\r\n"
1146 "valid values for <state> are the following:\r\n\r\n" );
1147 for (nn = 0; ; nn++) {
1148 const char* name = _gsm_states[nn].name;
1149 const char* display = _gsm_states[nn].display;
1150
1151 if (!name)
1152 break;
1153
1154 control_write( client, " %-15s %s\r\n", name, display );
1155 }
1156 control_write( client, "\r\n" );
1157}
1158
1159
1160static int
1161do_gsm_data( ControlClient client, char* args )
1162{
1163 int nn;
1164
1165 if (!args) {
1166 control_write( client, "KO: missing argument, try 'gsm data <state>'\r\n" );
1167 return -1;
1168 }
1169
1170 for (nn = 0; ; nn++) {
1171 const char* name = _gsm_states[nn].name;
1172 ARegistrationState state = _gsm_states[nn].state;
1173
1174 if (!name)
1175 break;
1176
1177 if ( !strcmp( args, name ) ) {
1178 if (!android_modem) {
1179 control_write( client, "KO: modem emulation not running\r\n" );
1180 return -1;
1181 }
1182 amodem_set_data_registration( android_modem, state );
1183 qemu_net_disable = (state != A_REGISTRATION_HOME &&
1184 state != A_REGISTRATION_ROAMING );
1185 return 0;
1186 }
1187 }
1188 control_write( client, "KO: bad GSM data state name, try 'help gsm data' for list of valid values\r\n" );
1189 return -1;
1190}
1191
1192static void
1193help_gsm_voice( ControlClient client )
1194{
1195 int nn;
1196 control_write( client,
1197 "the 'gsm voice <state>' allows you to change the state of your GPRS connection\r\n"
1198 "valid values for <state> are the following:\r\n\r\n" );
1199 for (nn = 0; ; nn++) {
1200 const char* name = _gsm_states[nn].name;
1201 const char* display = _gsm_states[nn].display;
1202
1203 if (!name)
1204 break;
1205
1206 control_write( client, " %-15s %s\r\n", name, display );
1207 }
1208 control_write( client, "\r\n" );
1209}
1210
1211
1212static int
1213do_gsm_voice( ControlClient client, char* args )
1214{
1215 int nn;
1216
1217 if (!args) {
1218 control_write( client, "KO: missing argument, try 'gsm voice <state>'\r\n" );
1219 return -1;
1220 }
1221
1222 for (nn = 0; ; nn++) {
1223 const char* name = _gsm_states[nn].name;
1224 ARegistrationState state = _gsm_states[nn].state;
1225
1226 if (!name)
1227 break;
1228
1229 if ( !strcmp( args, name ) ) {
1230 if (!android_modem) {
1231 control_write( client, "KO: modem emulation not running\r\n" );
1232 return -1;
1233 }
1234 amodem_set_voice_registration( android_modem, state );
1235 return 0;
1236 }
1237 }
1238 control_write( client, "KO: bad GSM data state name, try 'help gsm voice' for list of valid values\r\n" );
1239 return -1;
1240}
1241
1242
1243static int
1244gsm_check_number( char* args )
1245{
1246 int nn;
1247
1248 for (nn = 0; args[nn] != 0; nn++) {
1249 int c = args[nn];
1250 if ( !isdigit(c) && c != '+' && c != '#' ) {
1251 return -1;
1252 }
1253 }
1254 if (nn == 0)
1255 return -1;
1256
1257 return 0;
1258}
1259
1260static int
1261do_gsm_call( ControlClient client, char* args )
1262{
1263 /* check that we have a phone number made of digits */
1264 if (!args) {
1265 control_write( client, "KO: missing argument, try 'gsm call <phonenumber>'\r\n" );
1266 return -1;
1267 }
1268
1269 if (gsm_check_number(args)) {
1270 control_write( client, "KO: bad phone number format, use digits, # and + only\r\n" );
1271 return -1;
1272 }
1273
1274 if (!android_modem) {
1275 control_write( client, "KO: modem emulation not running\r\n" );
1276 return -1;
1277 }
1278 amodem_add_inbound_call( android_modem, args );
1279 return 0;
1280}
1281
1282static int
1283do_gsm_cancel( ControlClient client, char* args )
1284{
1285 if (!args) {
1286 control_write( client, "KO: missing argument, try 'gsm call <phonenumber>'\r\n" );
1287 return -1;
1288 }
1289 if (gsm_check_number(args)) {
1290 control_write( client, "KO: bad phone number format, use digits, # and + only\r\n" );
1291 return -1;
1292 }
1293 if (!android_modem) {
1294 control_write( client, "KO: modem emulation not running\r\n" );
1295 return -1;
1296 }
1297 if ( amodem_disconnect_call( android_modem, args ) < 0 ) {
1298 control_write( client, "KO: could not cancel this number\r\n" );
1299 return -1;
1300 }
1301 return 0;
1302}
1303
1304
1305static const char*
1306call_state_to_string( ACallState state )
1307{
1308 switch (state) {
1309 case A_CALL_ACTIVE: return "active";
1310 case A_CALL_HELD: return "held";
1311 case A_CALL_ALERTING: return "ringing";
1312 case A_CALL_WAITING: return "waiting";
1313 case A_CALL_INCOMING: return "incoming";
1314 default: return "unknown";
1315 }
1316}
1317
1318static int
1319do_gsm_list( ControlClient client, char* args )
1320{
1321 /* check that we have a phone number made of digits */
1322 int count = amodem_get_call_count( android_modem );
1323 int nn;
1324 for (nn = 0; nn < count; nn++) {
1325 ACall call = amodem_get_call( android_modem, nn );
1326 const char* dir;
1327
1328 if (call == NULL)
1329 continue;
1330
1331 if (call->dir == A_CALL_OUTBOUND)
1332 dir = "outbound to ";
1333 else
1334 dir = "inbound from";
1335
1336 control_write( client, "%s %-10s : %s\r\n", dir,
1337 call->number, call_state_to_string(call->state) );
1338 }
1339 return 0;
1340}
1341
1342static int
1343do_gsm_busy( ControlClient client, char* args )
1344{
1345 ACall call;
1346
1347 if (!args) {
1348 control_write( client, "KO: missing argument, try 'gsm busy <phonenumber>'\r\n" );
1349 return -1;
1350 }
1351 call = amodem_find_call_by_number( android_modem, args );
1352 if (call == NULL || call->dir != A_CALL_OUTBOUND) {
1353 control_write( client, "KO: no current outbound call to number '%s' (call %p)\r\n", args, call );
1354 return -1;
1355 }
1356 if ( amodem_disconnect_call( android_modem, args ) < 0 ) {
1357 control_write( client, "KO: could not cancel this number\r\n" );
1358 return -1;
1359 }
1360 return 0;
1361}
1362
1363static int
1364do_gsm_hold( ControlClient client, char* args )
1365{
1366 ACall call;
1367
1368 if (!args) {
1369 control_write( client, "KO: missing argument, try 'gsm out hold <phonenumber>'\r\n" );
1370 return -1;
1371 }
1372 call = amodem_find_call_by_number( android_modem, args );
1373 if (call == NULL) {
1374 control_write( client, "KO: no current call to/from number '%s'\r\n", args );
1375 return -1;
1376 }
1377 if ( amodem_update_call( android_modem, args, A_CALL_HELD ) < 0 ) {
1378 control_write( client, "KO: could put this call on hold\r\n" );
1379 return -1;
1380 }
1381 return 0;
1382}
1383
1384
1385static int
1386do_gsm_accept( ControlClient client, char* args )
1387{
1388 ACall call;
1389
1390 if (!args) {
1391 control_write( client, "KO: missing argument, try 'gsm accept <phonenumber>'\r\n" );
1392 return -1;
1393 }
1394 call = amodem_find_call_by_number( android_modem, args );
1395 if (call == NULL) {
1396 control_write( client, "KO: no current call to/from number '%s'\r\n", args );
1397 return -1;
1398 }
1399 if ( amodem_update_call( android_modem, args, A_CALL_ACTIVE ) < 0 ) {
1400 control_write( client, "KO: could not activate this call\r\n" );
1401 return -1;
1402 }
1403 return 0;
1404}
1405
Tim Baverstock4c6b10a2010-12-15 17:31:13 +00001406static int
1407do_gsm_signal( ControlClient client, char* args )
1408{
1409 enum { SIGNAL_RSSI = 0, SIGNAL_BER, NUM_SIGNAL_PARAMS };
1410 char* p = args;
1411 int top_param = -1;
1412 int params[ NUM_SIGNAL_PARAMS ];
1413
1414 static int last_ber = 99;
1415
1416 if (!p)
1417 p = "";
1418
1419 /* tokenize */
1420 while (*p) {
1421 char* end;
1422 int val = strtol( p, &end, 10 );
1423
1424 if (end == p) {
1425 control_write( client, "KO: argument '%s' is not a number\n", p );
1426 return -1;
1427 }
1428
1429 params[++top_param] = val;
1430 if (top_param + 1 == NUM_SIGNAL_PARAMS)
1431 break;
1432
1433 p = end;
1434 while (*p && (p[0] == ' ' || p[0] == '\t'))
1435 p += 1;
1436 }
1437
1438 /* sanity check */
1439 if (top_param < SIGNAL_RSSI) {
1440 control_write( client, "KO: not enough arguments: see 'help gsm signal' for details\r\n" );
1441 return -1;
1442 }
1443
1444 int rssi = params[SIGNAL_RSSI];
1445 if ((rssi < 0 || rssi > 31) && rssi != 99) {
1446 control_write( client, "KO: invalid RSSI - must be 0..31 or 99\r\n");
1447 return -1;
1448 }
1449
1450 /* check ber is 0..7 or 99 */
1451 if (top_param >= SIGNAL_BER) {
1452 int ber = params[SIGNAL_BER];
1453 if ((ber < 0 || ber > 7) && ber != 99) {
1454 control_write( client, "KO: invalid BER - must be 0..7 or 99\r\n");
1455 return -1;
1456 }
1457 last_ber = ber;
1458 }
1459
1460 amodem_set_signal_strength( android_modem, rssi, last_ber );
1461
1462 return 0;
1463 }
1464
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001465
1466#if 0
1467static const CommandDefRec gsm_in_commands[] =
1468{
1469 { "new", "create a new 'waiting' inbound call",
1470 "'gsm in create <phonenumber>' creates a new inbound phone call, placed in\r\n"
1471 "the 'waiting' state by default, until the system answers/holds/closes it\r\n", NULL
1472 do_gsm_in_create, NULL },
1473
1474 { "hold", "change the state of an oubtound call to 'held'",
1475 "change the state of an outbound call to 'held'. this is only possible\r\n"
1476 "if the call in the 'waiting' or 'active' state\r\n", NULL,
1477 do_gsm_out_hold, NULL },
1478
1479 { "accept", "change the state of an outbound call to 'active'",
1480 "change the state of an outbound call to 'active'. this is only possible\r\n"
1481 "if the call is in the 'waiting' or 'held' state\r\n", NULL,
1482 do_gsm_out_accept, NULL },
1483
1484 { NULL, NULL, NULL, NULL, NULL, NULL }
1485};
1486#endif
1487
1488
Jaime Lopez1a000852010-07-21 18:03:58 -07001489static const CommandDefRec cdma_commands[] =
1490{
1491 { "ssource", "Set the current CDMA subscription source",
1492 NULL, describe_subscription_source,
1493 do_cdma_ssource, NULL },
David 'Digit' Turner80bc5c82010-10-20 19:04:51 +02001494 { "prl_version", "Dump the current PRL version",
1495 NULL, NULL,
1496 do_cdma_prl_version, NULL },
Jaime Lopez1a000852010-07-21 18:03:58 -07001497};
1498
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001499static const CommandDefRec gsm_commands[] =
1500{
1501 { "list", "list current phone calls",
1502 "'gsm list' lists all inbound and outbound calls and their state\r\n", NULL,
1503 do_gsm_list, NULL },
1504
1505 { "call", "create inbound phone call",
1506 "'gsm call <phonenumber>' allows you to simulate a new inbound call\r\n", NULL,
1507 do_gsm_call, NULL },
1508
1509 { "busy", "close waiting outbound call as busy",
1510 "'gsm busy <remoteNumber>' closes an outbound call, reporting\r\n"
1511 "the remote phone as busy. only possible if the call is 'waiting'.\r\n", NULL,
1512 do_gsm_busy, NULL },
1513
1514 { "hold", "change the state of an oubtound call to 'held'",
1515 "'gsm hold <remoteNumber>' change the state of a call to 'held'. this is only possible\r\n"
1516 "if the call in the 'waiting' or 'active' state\r\n", NULL,
1517 do_gsm_hold, NULL },
1518
1519 { "accept", "change the state of an outbound call to 'active'",
1520 "'gsm accept <remoteNumber>' change the state of a call to 'active'. this is only possible\r\n"
1521 "if the call is in the 'waiting' or 'held' state\r\n", NULL,
1522 do_gsm_accept, NULL },
1523
1524 { "cancel", "disconnect an inbound or outbound phone call",
1525 "'gsm cancel <phonenumber>' allows you to simulate the end of an inbound or outbound call\r\n", NULL,
1526 do_gsm_cancel, NULL },
1527
1528 { "data", "modify data connection state", NULL, help_gsm_data,
1529 do_gsm_data, NULL },
1530
1531 { "voice", "modify voice connection state", NULL, help_gsm_voice,
1532 do_gsm_voice, NULL },
1533
1534 { "status", "display GSM status",
1535 "'gsm status' displays the current state of the GSM emulation\r\n", NULL,
1536 do_gsm_status, NULL },
1537
Tim Baverstock4c6b10a2010-12-15 17:31:13 +00001538 { "signal", "set sets the rssi and ber",
1539 "'gsm signal <rssi> [<ber>]' changes the reported strength and error rate on next (15s) update.\r\n"
1540 "rssi range is 0..31 and 99 for unknown\r\n"
1541 "ber range is 0..7 percent and 99 for unknown\r\n",
1542 NULL, do_gsm_signal, NULL },
1543
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001544 { NULL, NULL, NULL, NULL, NULL, NULL }
1545};
1546
1547/********************************************************************************************/
1548/********************************************************************************************/
1549/***** ******/
1550/***** S M S C O M M A N D ******/
1551/***** ******/
1552/********************************************************************************************/
1553/********************************************************************************************/
1554
1555static int
1556do_sms_send( ControlClient client, char* args )
1557{
1558 char* p;
1559 int textlen;
1560 SmsAddressRec sender;
1561 SmsPDU* pdus;
1562 int nn;
1563
1564 /* check that we have a phone number made of digits */
1565 if (!args) {
1566 MissingArgument:
1567 control_write( client, "KO: missing argument, try 'sms send <phonenumber> <text message>'\r\n" );
1568 return -1;
1569 }
1570 p = strchr( args, ' ' );
1571 if (!p) {
1572 goto MissingArgument;
1573 }
1574
1575 if ( sms_address_from_str( &sender, args, p - args ) < 0 ) {
1576 control_write( client, "KO: bad phone number format, must be [+](0-9)*\r\n" );
1577 return -1;
1578 }
1579
1580
1581 /* un-secape message text into proper utf-8 (conversion happens in-site) */
1582 p += 1;
1583 textlen = strlen(p);
1584 textlen = sms_utf8_from_message_str( p, textlen, (unsigned char*)p, textlen );
1585 if (textlen < 0) {
1586 control_write( client, "message must be utf8 and can use the following escapes:\r\n"
1587 " \\n for a newline\r\n"
1588 " \\xNN where NN are two hexadecimal numbers\r\n"
1589 " \\uNNNN where NNNN are four hexadecimal numbers\r\n"
1590 " \\\\ to send a '\\' character\r\n\r\n"
1591 " anything else is an error\r\n"
1592 "KO: badly formatted text\r\n" );
1593 return -1;
1594 }
1595
1596 if (!android_modem) {
1597 control_write( client, "KO: modem emulation not running\r\n" );
1598 return -1;
1599 }
1600
1601 /* create a list of SMS PDUs, then send them */
1602 pdus = smspdu_create_deliver_utf8( (cbytes_t)p, textlen, &sender, NULL );
1603 if (pdus == NULL) {
1604 control_write( client, "KO: internal error when creating SMS-DELIVER PDUs\n" );
1605 return -1;
1606 }
1607
1608 for (nn = 0; pdus[nn] != NULL; nn++)
1609 amodem_receive_sms( android_modem, pdus[nn] );
1610
1611 smspdu_free_list( pdus );
1612 return 0;
1613}
1614
1615static int
1616do_sms_sendpdu( ControlClient client, char* args )
1617{
1618 SmsPDU pdu;
1619
1620 /* check that we have a phone number made of digits */
1621 if (!args) {
1622 control_write( client, "KO: missing argument, try 'sms sendpdu <hexstring>'\r\n" );
1623 return -1;
1624 }
1625
1626 if (!android_modem) {
1627 control_write( client, "KO: modem emulation not running\r\n" );
1628 return -1;
1629 }
1630
1631 pdu = smspdu_create_from_hex( args, strlen(args) );
1632 if (pdu == NULL) {
1633 control_write( client, "KO: badly formatted <hexstring>\r\n" );
1634 return -1;
1635 }
1636
1637 amodem_receive_sms( android_modem, pdu );
1638 smspdu_free( pdu );
1639 return 0;
1640}
1641
1642static const CommandDefRec sms_commands[] =
1643{
1644 { "send", "send inbound SMS text message",
1645 "'sms send <phonenumber> <message>' allows you to simulate a new inbound sms message\r\n", NULL,
1646 do_sms_send, NULL },
1647
1648 { "pdu", "send inbound SMS PDU",
1649 "'sms pdu <hexstring>' allows you to simulate a new inbound sms PDU\r\n"
1650 "(used internally when one emulator sends SMS messages to another instance).\r\n"
1651 "you probably don't want to play with this at all\r\n", NULL,
1652 do_sms_sendpdu, NULL },
1653
1654 { NULL, NULL, NULL, NULL, NULL, NULL }
1655};
1656
1657static void
1658do_control_write(void* data, const char* string)
1659{
1660 control_write((ControlClient)data, string);
1661}
1662
1663static int
1664do_power_display( ControlClient client, char* args )
1665{
1666 goldfish_battery_display(do_control_write, client);
1667 return 0;
1668}
1669
1670static int
1671do_ac_state( ControlClient client, char* args )
1672{
1673 if (args) {
1674 if (strcasecmp(args, "on") == 0) {
1675 goldfish_battery_set_prop(1, POWER_SUPPLY_PROP_ONLINE, 1);
1676 return 0;
1677 }
1678 if (strcasecmp(args, "off") == 0) {
1679 goldfish_battery_set_prop(1, POWER_SUPPLY_PROP_ONLINE, 0);
1680 return 0;
1681 }
1682 }
1683
1684 control_write( client, "KO: Usage: \"ac on\" or \"ac off\"\n" );
1685 return -1;
1686}
1687
1688static int
1689do_battery_status( ControlClient client, char* args )
1690{
1691 if (args) {
1692 if (strcasecmp(args, "unknown") == 0) {
1693 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_UNKNOWN);
1694 return 0;
1695 }
1696 if (strcasecmp(args, "charging") == 0) {
1697 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_CHARGING);
1698 return 0;
1699 }
1700 if (strcasecmp(args, "discharging") == 0) {
1701 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_DISCHARGING);
1702 return 0;
1703 }
1704 if (strcasecmp(args, "not-charging") == 0) {
1705 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_NOT_CHARGING);
1706 return 0;
1707 }
1708 if (strcasecmp(args, "full") == 0) {
1709 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_FULL);
1710 return 0;
1711 }
1712 }
1713
1714 control_write( client, "KO: Usage: \"status unknown|charging|discharging|not-charging|full\"\n" );
1715 return -1;
1716}
1717
1718static int
1719do_battery_present( ControlClient client, char* args )
1720{
1721 if (args) {
1722 if (strcasecmp(args, "true") == 0) {
1723 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_PRESENT, 1);
1724 return 0;
1725 }
1726 if (strcasecmp(args, "false") == 0) {
1727 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_PRESENT, 0);
1728 return 0;
1729 }
1730 }
1731
1732 control_write( client, "KO: Usage: \"present true\" or \"present false\"\n" );
1733 return -1;
1734}
1735
1736static int
1737do_battery_health( ControlClient client, char* args )
1738{
1739 if (args) {
1740 if (strcasecmp(args, "unknown") == 0) {
1741 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_UNKNOWN);
1742 return 0;
1743 }
1744 if (strcasecmp(args, "good") == 0) {
1745 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_GOOD);
1746 return 0;
1747 }
1748 if (strcasecmp(args, "overheat") == 0) {
1749 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_OVERHEAT);
1750 return 0;
1751 }
1752 if (strcasecmp(args, "dead") == 0) {
1753 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_DEAD);
1754 return 0;
1755 }
1756 if (strcasecmp(args, "overvoltage") == 0) {
1757 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_OVERVOLTAGE);
1758 return 0;
1759 }
1760 if (strcasecmp(args, "failure") == 0) {
1761 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_UNSPEC_FAILURE);
1762 return 0;
1763 }
1764 }
1765
1766 control_write( client, "KO: Usage: \"health unknown|good|overheat|dead|overvoltage|failure\"\n" );
1767 return -1;
1768}
1769
1770static int
1771do_battery_capacity( ControlClient client, char* args )
1772{
1773 if (args) {
1774 int capacity;
1775
1776 if (sscanf(args, "%d", &capacity) == 1 && capacity >= 0 && capacity <= 100) {
1777 goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_CAPACITY, capacity);
1778 return 0;
1779 }
1780 }
1781
1782 control_write( client, "KO: Usage: \"capacity <percentage>\"\n" );
1783 return -1;
1784}
1785
1786
1787static const CommandDefRec power_commands[] =
1788{
1789 { "display", "display battery and charger state",
1790 "display battery and charger state\r\n", NULL,
1791 do_power_display, NULL },
1792
1793 { "ac", "set AC charging state",
1794 "'ac on|off' allows you to set the AC charging state to on or off\r\n", NULL,
1795 do_ac_state, NULL },
1796
1797 { "status", "set battery status",
1798 "'status unknown|charging|discharging|not-charging|full' allows you to set battery status\r\n", NULL,
1799 do_battery_status, NULL },
1800
1801 { "present", "set battery present state",
1802 "'present true|false' allows you to set battery present state to true or false\r\n", NULL,
1803 do_battery_present, NULL },
1804
1805 { "health", "set battery health state",
1806 "'health unknown|good|overheat|dead|overvoltage|failure' allows you to set battery health state\r\n", NULL,
1807 do_battery_health, NULL },
1808
1809 { "capacity", "set battery capacity state",
1810 "'capacity <percentage>' allows you to set battery capacity to a value 0 - 100\r\n", NULL,
1811 do_battery_capacity, NULL },
1812
1813 { NULL, NULL, NULL, NULL, NULL, NULL }
1814};
1815
1816/********************************************************************************************/
1817/********************************************************************************************/
1818/***** ******/
1819/***** E V E N T C O M M A N D S ******/
1820/***** ******/
1821/********************************************************************************************/
1822/********************************************************************************************/
1823
1824
1825static int
1826do_event_send( ControlClient client, char* args )
1827{
1828 char* p;
1829
1830 if (!args) {
1831 control_write( client, "KO: Usage: event send <type>:<code>:<value> ...\r\n" );
1832 return -1;
1833 }
1834
1835 p = args;
1836 while (*p) {
1837 char* q;
David 'Digit' Turner88935f72011-05-09 10:24:18 +02001838 char temp[128];
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001839 int type, code, value, ret;
1840
David 'Digit' Turner88935f72011-05-09 10:24:18 +02001841 p += strspn( p, " \t" ); /* skip spaces */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001842 if (*p == 0)
1843 break;
1844
1845 q = p + strcspn( p, " \t" );
1846
1847 if (q == p)
1848 break;
1849
Andrew Hsiehc7389bd2012-03-13 02:13:40 -07001850 snprintf(temp, sizeof temp, "%.*s", (int)(intptr_t)(q-p), p);
David 'Digit' Turner88935f72011-05-09 10:24:18 +02001851 ret = android_event_from_str( temp, &type, &code, &value );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001852 if (ret < 0) {
1853 if (ret == -1) {
1854 control_write( client,
1855 "KO: invalid event type in '%.*s', try 'event list types' for valid values\r\n",
1856 q-p, p );
1857 } else if (ret == -2) {
1858 control_write( client,
1859 "KO: invalid event code in '%.*s', try 'event list codes <type>' for valid values\r\n",
1860 q-p, p );
1861 } else {
1862 control_write( client,
1863 "KO: invalid event value in '%.*s', must be an integer\r\n",
1864 q-p, p);
1865 }
1866 return -1;
1867 }
1868
David 'Digit' Turner34f29742010-05-25 18:16:10 -07001869 user_event_generic( type, code, value );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001870 p = q;
1871 }
1872 return 0;
1873}
1874
1875static int
1876do_event_types( ControlClient client, char* args )
1877{
1878 int count = android_event_get_type_count();
1879 int nn;
1880
1881 control_write( client, "event <type> can be an integer or one of the following aliases\r\n" );
1882 for (nn = 0; nn < count; nn++) {
1883 char tmp[16];
1884 char* p = tmp;
1885 char* end = p + sizeof(tmp);
1886 int count2 = android_event_get_code_count( nn );;
1887
1888 p = android_event_bufprint_type_str( p, end, nn );
1889
1890 control_write( client, " %-8s", tmp );
1891 if (count2 > 0)
1892 control_write( client, " (%d code aliases)", count2 );
1893
1894 control_write( client, "\r\n" );
1895 }
1896 return 0;
1897}
1898
1899static int
1900do_event_codes( ControlClient client, char* args )
1901{
1902 int count;
1903 int nn, type, dummy;
1904
1905 if (!args) {
1906 control_write( client, "KO: argument missing, try 'event codes <type>'\r\n" );
1907 return -1;
1908 }
1909
1910 if ( android_event_from_str( args, &type, &dummy, &dummy ) < 0 ) {
1911 control_write( client, "KO: bad argument, see 'event types' for valid values\r\n" );
1912 return -1;
1913 }
1914
1915 count = android_event_get_code_count( type );
1916 if (count == 0) {
1917 control_write( client, "no code aliases defined for this type\r\n" );
1918 } else {
1919 control_write( client, "type '%s' accepts the following <code> aliases:\r\n",
1920 args );
1921 for (nn = 0; nn < count; nn++) {
1922 char temp[20], *p = temp, *end = p + sizeof(temp);
1923 android_event_bufprint_code_str( p, end, type, nn );
1924 control_write( client, " %-12s\r\n", temp );
1925 }
1926 }
1927
1928 return 0;
1929}
1930
1931static __inline__ int
1932utf8_next( unsigned char* *pp, unsigned char* end )
1933{
1934 unsigned char* p = *pp;
1935 int result = -1;
1936
1937 if (p < end) {
1938 int c= *p++;
1939 if (c >= 128) {
1940 if ((c & 0xe0) == 0xc0)
1941 c &= 0x1f;
1942 else if ((c & 0xf0) == 0xe0)
1943 c &= 0x0f;
1944 else
1945 c &= 0x07;
1946
1947 while (p < end && (p[0] & 0xc0) == 0x80) {
1948 c = (c << 6) | (p[0] & 0x3f);
1949 }
1950 }
1951 result = c;
1952 *pp = p;
1953 }
1954 return result;
1955}
1956
1957static int
1958do_event_text( ControlClient client, char* args )
1959{
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001960 AKeycodeBuffer keycodes;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001961 unsigned char* p = (unsigned char*) args;
1962 unsigned char* end = p + strlen(args);
1963 int textlen;
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001964 const AKeyCharmap* charmap;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001965
1966 if (!args) {
1967 control_write( client, "KO: argument missing, try 'event text <message>'\r\n" );
1968 return -1;
1969 }
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001970
David 'Digit' Turner0158ea32011-01-19 05:21:31 +01001971 /* Get active charmap. */
1972 charmap = android_get_charmap();
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001973 if (charmap == NULL) {
1974 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 -08001975 return -1;
1976 }
1977
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07001978 keycodes.keycode_count = 0;
1979
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001980 /* un-secape message text into proper utf-8 (conversion happens in-site) */
1981 textlen = strlen((char*)p);
1982 textlen = sms_utf8_from_message_str( args, textlen, (unsigned char*)p, textlen );
1983 if (textlen < 0) {
1984 control_write( client, "message must be utf8 and can use the following escapes:\r\n"
1985 " \\n for a newline\r\n"
1986 " \\xNN where NN are two hexadecimal numbers\r\n"
1987 " \\uNNNN where NNNN are four hexadecimal numbers\r\n"
1988 " \\\\ to send a '\\' character\r\n\r\n"
1989 " anything else is an error\r\n"
1990 "KO: badly formatted text\r\n" );
1991 return -1;
1992 }
1993
1994 end = p + textlen;
1995 while (p < end) {
1996 int c = utf8_next( &p, end );
1997 if (c <= 0)
1998 break;
1999
Vladimir Chtchetkine71bb14f2010-07-07 15:57:00 -07002000 android_charmap_reverse_map_unicode( NULL, (unsigned)c, 1, &keycodes );
2001 android_charmap_reverse_map_unicode( NULL, (unsigned)c, 0, &keycodes );
2002 android_keycodes_flush( &keycodes );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002003 }
2004
2005 return 0;
2006}
2007
2008static const CommandDefRec event_commands[] =
2009{
2010 { "send", "send a series of events to the kernel",
2011 "'event send <type>:<code>:<value> ...' allows your to send one or more hardware events\r\n"
2012 "to the Android kernel. you can use text names or integers for <type> and <code>\r\n", NULL,
2013 do_event_send, NULL },
2014
2015 { "types", "list all <type> aliases",
2016 "'event types' list all <type> string aliases supported by the 'event' subcommands\r\n",
2017 NULL, do_event_types, NULL },
2018
2019 { "codes", "list all <code> aliases for a given <type>",
2020 "'event codes <type>' lists all <code> string aliases for a given event <type>\r\n",
2021 NULL, do_event_codes, NULL },
2022
2023 { "text", "simulate keystrokes from a given text",
2024 "'event text <message>' allows you to simulate keypresses to generate a given text\r\n"
2025 "message. <message> must be an utf-8 string. Unicode points will be reverse-mapped\r\n"
2026 "according to the current device keyboard. unsupported characters will be discarded\r\n"
2027 "silently\r\n", NULL, do_event_text, NULL },
2028
2029 { NULL, NULL, NULL, NULL, NULL, NULL }
2030};
2031
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002032
2033/********************************************************************************************/
2034/********************************************************************************************/
2035/***** ******/
2036/***** S N A P S H O T C O M M A N D S ******/
2037/***** ******/
2038/********************************************************************************************/
2039/********************************************************************************************/
2040
2041static int
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002042control_write_out_cb(void* opaque, const char* str, int strsize)
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002043{
2044 ControlClient client = opaque;
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002045 control_control_write(client, str, strsize);
2046 return strsize;
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002047}
2048
2049static int
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002050control_write_err_cb(void* opaque, const char* str, int strsize)
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002051{
2052 int ret = 0;
2053 ControlClient client = opaque;
2054 ret += control_write(client, "KO: ");
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002055 control_control_write(client, str, strsize);
2056 return ret + strsize;
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002057}
2058
2059static int
2060do_snapshot_list( ControlClient client, char* args )
2061{
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002062 int64_t ret;
2063 Monitor *out = monitor_fake_new(client, control_write_out_cb);
2064 Monitor *err = monitor_fake_new(client, control_write_err_cb);
2065 do_info_snapshots(out, err);
2066 ret = monitor_fake_get_bytes(err);
2067 monitor_fake_free(err);
2068 monitor_fake_free(out);
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002069
2070 return ret > 0;
2071}
2072
2073static int
2074do_snapshot_save( ControlClient client, char* args )
2075{
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002076 int64_t ret;
David 'Digit' Turner9fb360e2011-05-04 22:01:28 +02002077
2078 if (args == NULL) {
2079 control_write(client, "KO: argument missing, try 'avd snapshot save <name>'\r\n");
2080 return -1;
2081 }
2082
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002083 Monitor *err = monitor_fake_new(client, control_write_err_cb);
2084 do_savevm(err, args);
2085 ret = monitor_fake_get_bytes(err);
2086 monitor_fake_free(err);
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002087
2088 return ret > 0; // no output on error channel indicates success
2089}
2090
2091static int
2092do_snapshot_load( ControlClient client, char* args )
2093{
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002094 int64_t ret;
David 'Digit' Turner9fb360e2011-05-04 22:01:28 +02002095
2096 if (args == NULL) {
2097 control_write(client, "KO: argument missing, try 'avd snapshot load <name>'\r\n");
2098 return -1;
2099 }
2100
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002101 Monitor *err = monitor_fake_new(client, control_write_err_cb);
2102 do_loadvm(err, args);
2103 ret = monitor_fake_get_bytes(err);
2104 monitor_fake_free(err);
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002105
2106 return ret > 0;
2107}
2108
2109static int
2110do_snapshot_del( ControlClient client, char* args )
2111{
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002112 int64_t ret;
David 'Digit' Turner9fb360e2011-05-04 22:01:28 +02002113
2114 if (args == NULL) {
2115 control_write(client, "KO: argument missing, try 'avd snapshot del <name>'\r\n");
2116 return -1;
2117 }
2118
David 'Digit' Turner95a83ce2011-05-10 17:31:15 +02002119 Monitor *err = monitor_fake_new(client, control_write_err_cb);
2120 do_delvm(err, args);
2121 ret = monitor_fake_get_bytes(err);
2122 monitor_fake_free(err);
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002123
2124 return ret > 0;
2125}
2126
2127static const CommandDefRec snapshot_commands[] =
2128{
2129 { "list", "list available state snapshots",
2130 "'avd snapshot list' will show a list of all state snapshots that can be loaded\r\n",
2131 NULL, do_snapshot_list, NULL },
2132
2133 { "save", "save state snapshot",
2134 "'avd snapshot save <name>' will save the current (run-time) state to a snapshot with the given name\r\n",
2135 NULL, do_snapshot_save, NULL },
2136
2137 { "load", "load state snapshot",
2138 "'avd snapshot load <name>' will load the state snapshot of the given name\r\n",
2139 NULL, do_snapshot_load, NULL },
2140
2141 { "del", "delete state snapshot",
2142 "'avd snapshot del <name>' will delete the state snapshot with the given name\r\n",
2143 NULL, do_snapshot_del, NULL },
2144
2145 { NULL, NULL, NULL, NULL, NULL, NULL }
2146};
2147
2148
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002149
2150/********************************************************************************************/
2151/********************************************************************************************/
2152/***** ******/
2153/***** V M C O M M A N D S ******/
2154/***** ******/
2155/********************************************************************************************/
2156/********************************************************************************************/
2157
2158static int
2159do_avd_stop( ControlClient client, char* args )
2160{
2161 if (!vm_running) {
2162 control_write( client, "KO: virtual device already stopped\r\n" );
2163 return -1;
2164 }
2165 vm_stop(EXCP_INTERRUPT);
2166 return 0;
2167}
2168
2169static int
2170do_avd_start( ControlClient client, char* args )
2171{
2172 if (vm_running) {
2173 control_write( client, "KO: virtual device already running\r\n" );
2174 return -1;
2175 }
2176 vm_start();
2177 return 0;
2178}
2179
2180static int
2181do_avd_status( ControlClient client, char* args )
2182{
2183 control_write( client, "virtual device is %s\r\n", vm_running ? "running" : "stopped" );
2184 return 0;
2185}
2186
2187static int
2188do_avd_name( ControlClient client, char* args )
2189{
David 'Digit' Turnerec6cedb2011-05-05 12:49:51 +02002190 control_write( client, "%s\r\n", android_hw->avd_name);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002191 return 0;
2192}
2193
2194static const CommandDefRec vm_commands[] =
2195{
2196 { "stop", "stop the virtual device",
2197 "'avd stop' stops the virtual device immediately, use 'avd start' to continue execution\r\n",
2198 NULL, do_avd_stop, NULL },
2199
2200 { "start", "start/restart the virtual device",
2201 "'avd start' will start or continue the virtual device, use 'avd stop' to stop it\r\n",
2202 NULL, do_avd_start, NULL },
2203
2204 { "status", "query virtual device status",
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002205 "'avd status' will indicate whether the virtual device is running or not\r\n",
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002206 NULL, do_avd_status, NULL },
2207
2208 { "name", "query virtual device name",
2209 "'avd name' will return the name of this virtual device\r\n",
2210 NULL, do_avd_name, NULL },
2211
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002212 { "snapshot", "state snapshot commands",
2213 "allows you to save and restore the virtual device state in snapshots\r\n",
2214 NULL, NULL, snapshot_commands },
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002215
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002216 { NULL, NULL, NULL, NULL, NULL, NULL }
2217};
2218
2219/********************************************************************************************/
2220/********************************************************************************************/
2221/***** ******/
2222/***** G E O C O M M A N D S ******/
2223/***** ******/
2224/********************************************************************************************/
2225/********************************************************************************************/
2226
2227static int
2228do_geo_nmea( ControlClient client, char* args )
2229{
2230 if (!args) {
2231 control_write( client, "KO: NMEA sentence missing, try 'help geo nmea'\r\n" );
2232 return -1;
2233 }
2234 if (!android_gps_cs) {
2235 control_write( client, "KO: no GPS emulation in this virtual device\r\n" );
2236 return -1;
2237 }
2238 android_gps_send_nmea( args );
2239 return 0;
2240}
2241
2242static int
2243do_geo_fix( ControlClient client, char* args )
2244{
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002245 // GEO_SAT2 provides bug backwards compatibility.
2246 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 -08002247 char* p = args;
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002248 int top_param = -1;
2249 double params[ NUM_GEO_PARAMS ];
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002250 int n_satellites = 1;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002251
David 'Digit' Turnera2c14f92014-02-04 01:02:30 +01002252 static int last_time = 0;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002253
2254 if (!p)
2255 p = "";
2256
2257 /* tokenize */
2258 while (*p) {
2259 char* end;
2260 double val = strtod( p, &end );
2261
2262 if (end == p) {
2263 control_write( client, "KO: argument '%s' is not a number\n", p );
2264 return -1;
2265 }
2266
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002267 params[++top_param] = val;
2268 if (top_param + 1 == NUM_GEO_PARAMS)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002269 break;
2270
2271 p = end;
2272 while (*p && (p[0] == ' ' || p[0] == '\t'))
2273 p += 1;
2274 }
2275
2276 /* sanity check */
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002277 if (top_param < GEO_LAT) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002278 control_write( client, "KO: not enough arguments: see 'help geo fix' for details\r\n" );
2279 return -1;
2280 }
2281
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002282 /* check number of satellites, must be integer between 1 and 12 */
2283 if (top_param >= GEO_SAT) {
2284 int sat_index = (top_param >= GEO_SAT2) ? GEO_SAT2 : GEO_SAT;
2285 n_satellites = (int) params[sat_index];
2286 if (n_satellites != params[sat_index]
2287 || n_satellites < 1 || n_satellites > 12) {
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002288 control_write( client, "KO: invalid number of satellites. Must be an integer between 1 and 12\r\n");
2289 return -1;
2290 }
2291 }
2292
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002293 /* generate an NMEA sentence for this fix */
2294 {
2295 STRALLOC_DEFINE(s);
2296 double val;
2297 int deg, min;
2298 char hemi;
2299
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002300 /* format overview:
2301 * time of fix 123519 12:35:19 UTC
2302 * latitude 4807.038 48 degrees, 07.038 minutes
2303 * north/south N or S
2304 * longitude 01131.000 11 degrees, 31. minutes
2305 * east/west E or W
2306 * fix quality 1 standard GPS fix
2307 * satellites 1 to 12 number of satellites being tracked
2308 * HDOP <dontcare> horizontal dilution
2309 * altitude 546. altitude above sea-level
2310 * altitude units M to indicate meters
2311 * diff <dontcare> height of sea-level above ellipsoid
2312 * diff units M to indicate meters (should be <dontcare>)
2313 * dgps age <dontcare> time in seconds since last DGPS fix
2314 * dgps sid <dontcare> DGPS station id
2315 */
2316
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002317 /* first, the time */
2318 stralloc_add_format( s, "$GPGGA,%06d", last_time );
2319 last_time ++;
2320
2321 /* then the latitude */
2322 hemi = 'N';
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002323 val = params[GEO_LAT];
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002324 if (val < 0) {
2325 hemi = 'S';
2326 val = -val;
2327 }
2328 deg = (int) val;
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002329 val = 60*(val - deg);
2330 min = (int) val;
David 'Digit' Turner631f2552010-10-27 02:46:53 +02002331 val = 10000*(val - min);
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002332 stralloc_add_format( s, ",%02d%02d.%04d,%c", deg, min, (int)val, hemi );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002333
2334 /* the longitude */
2335 hemi = 'E';
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002336 val = params[GEO_LONG];
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002337 if (val < 0) {
2338 hemi = 'W';
2339 val = -val;
2340 }
2341 deg = (int) val;
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002342 val = 60*(val - deg);
2343 min = (int) val;
David 'Digit' Turner631f2552010-10-27 02:46:53 +02002344 val = 10000*(val - min);
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002345 stralloc_add_format( s, ",%02d%02d.%04d,%c", deg, min, (int)val, hemi );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002346
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002347 /* bogus fix quality, satellite count and dilution */
2348 stralloc_add_format( s, ",1,%02d,", n_satellites );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002349
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002350 /* optional altitude + bogus diff */
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002351 if (top_param >= GEO_ALT) {
2352 stralloc_add_format( s, ",%.1g,M,0.,M", params[GEO_ALT] );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002353 } else {
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002354 stralloc_add_str( s, ",,,," );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002355 }
2356 /* bogus rest and checksum */
2357 stralloc_add_str( s, ",,,*47" );
2358
2359 /* send it, then free */
2360 android_gps_send_nmea( stralloc_cstr(s) );
2361 stralloc_reset( s );
2362 }
2363 return 0;
2364}
2365
2366static const CommandDefRec geo_commands[] =
2367{
2368 { "nmea", "send an GPS NMEA sentence",
2369 "'geo nema <sentence>' sends a NMEA 0183 sentence to the emulated device, as\r\n"
2370 "if it came from an emulated GPS modem. <sentence> must begin with '$GP'. only\r\n"
2371 "'$GPGGA' and '$GPRCM' sentences are supported at the moment.\r\n",
2372 NULL, do_geo_nmea, NULL },
2373
2374 { "fix", "send a simple GPS fix",
Tim Baverstock4afdaf12010-11-25 16:04:04 +00002375 "'geo fix <longitude> <latitude> [<altitude> [<satellites>]]'\r\n"
2376 " allows you to send a simple GPS fix to the emulated system.\r\n"
2377 " The parameters are:\r\n\r\n"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002378 " <longitude> longitude, in decimal degrees\r\n"
2379 " <latitude> latitude, in decimal degrees\r\n"
2380 " <altitude> optional altitude in meters\r\n"
David 'Digit' Turner657a3522010-07-23 16:24:16 -07002381 " <satellites> number of satellites being tracked (1-12)\r\n"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002382 "\r\n",
2383 NULL, do_geo_fix, NULL },
2384
2385 { NULL, NULL, NULL, NULL, NULL, NULL }
2386};
2387
2388
2389/********************************************************************************************/
2390/********************************************************************************************/
2391/***** ******/
Tim Wan736e01f2011-01-10 10:58:25 +01002392/***** S E N S O R S C O M M A N D S ******/
2393/***** ******/
2394/********************************************************************************************/
2395/********************************************************************************************/
2396
2397/* For sensors user prompt string size.*/
2398#define SENSORS_INFO_SIZE 150
2399
2400/* Get sensor data - (a,b,c) from sensor name */
2401static int
2402do_sensors_get( ControlClient client, char* args )
2403{
2404 if (! args) {
2405 control_write( client, "KO: Usage: \"get <sensorname>\"\n" );
2406 return -1;
2407 }
2408
2409 int status = SENSOR_STATUS_UNKNOWN;
2410 char sensor[strlen(args) + 1];
2411 if (1 != sscanf( args, "%s", &sensor[0] ))
2412 goto SENSOR_STATUS_ERROR;
2413
2414 int sensor_id = android_sensors_get_id_from_name( sensor );
2415 char buffer[SENSORS_INFO_SIZE] = { 0 };
2416 float a, b, c;
2417
2418 if (sensor_id < 0) {
2419 status = sensor_id;
2420 goto SENSOR_STATUS_ERROR;
2421 } else {
2422 status = android_sensors_get( sensor_id, &a, &b, &c );
2423 if (status != SENSOR_STATUS_OK)
2424 goto SENSOR_STATUS_ERROR;
2425 snprintf( buffer, sizeof(buffer),
2426 "%s = %g:%g:%g\r\n", sensor, a, b, c );
2427 do_control_write( client, buffer );
2428 return 0;
2429 }
2430
2431SENSOR_STATUS_ERROR:
2432 switch(status) {
2433 case SENSOR_STATUS_NO_SERVICE:
2434 snprintf( buffer, sizeof(buffer), "KO: No sensor service found!\r\n" );
2435 break;
2436 case SENSOR_STATUS_DISABLED:
2437 snprintf( buffer, sizeof(buffer), "KO: '%s' sensor is disabled.\r\n", sensor );
2438 break;
2439 case SENSOR_STATUS_UNKNOWN:
2440 snprintf( buffer, sizeof(buffer),
2441 "KO: unknown sensor name: %s, run 'sensor status' to get available sensors.\r\n", sensor );
2442 break;
2443 default:
2444 snprintf( buffer, sizeof(buffer), "KO: '%s' sensor: exception happens.\r\n", sensor );
2445 }
2446 do_control_write( client, buffer );
2447 return -1;
2448}
2449
2450/* set sensor data - (a,b,c) from sensor name */
2451static int
2452do_sensors_set( ControlClient client, char* args )
2453{
2454 if (! args) {
2455 control_write( client, "KO: Usage: \"set <sensorname> <value-a>[:<value-b>[:<value-c>]]\"\n" );
2456 return -1;
2457 }
2458
2459 int status;
2460 char* sensor;
2461 char* value;
2462 char* args_dup = strdup( args );
2463 if (args_dup == NULL) {
2464 control_write( client, "KO: Memory allocation failed.\n" );
2465 return -1;
2466 }
2467 char* p = args_dup;
2468
2469 /* Parsing the args to get sensor name string */
2470 while (*p && isspace(*p)) p++;
2471 if (*p == 0)
2472 goto INPUT_ERROR;
2473 sensor = p;
2474
2475 /* Parsing the args to get value string */
2476 while (*p && (! isspace(*p))) p++;
2477 if (*p == 0 || *(p + 1) == 0/* make sure value isn't NULL */)
2478 goto INPUT_ERROR;
2479 *p = 0;
2480 value = p + 1;
2481
2482 if (! (strlen(sensor) && strlen(value)))
2483 goto INPUT_ERROR;
2484
2485 int sensor_id = android_sensors_get_id_from_name( sensor );
2486 char buffer[SENSORS_INFO_SIZE] = { 0 };
2487
2488 if (sensor_id < 0) {
2489 status = sensor_id;
2490 goto SENSOR_STATUS_ERROR;
2491 } else {
2492 float fvalues[3];
2493 status = android_sensors_get( sensor_id, &fvalues[0], &fvalues[1], &fvalues[2] );
2494 if (status != SENSOR_STATUS_OK)
2495 goto SENSOR_STATUS_ERROR;
2496
2497 /* Parsing the value part to get the sensor values(a, b, c) */
2498 int i;
2499 char* pnext;
2500 char* pend = value + strlen(value);
2501 for (i = 0; i < 3; i++, value = pnext + 1) {
2502 pnext=strchr( value, ':' );
2503 if (pnext) {
2504 *pnext = 0;
2505 } else {
2506 pnext = pend;
2507 }
2508
2509 if (pnext > value) {
2510 if (1 != sscanf( value,"%g", &fvalues[i] ))
2511 goto INPUT_ERROR;
2512 }
2513 }
2514
2515 status = android_sensors_set( sensor_id, fvalues[0], fvalues[1], fvalues[2] );
2516 if (status != SENSOR_STATUS_OK)
2517 goto SENSOR_STATUS_ERROR;
2518
2519 free( args_dup );
2520 return 0;
2521 }
2522
2523SENSOR_STATUS_ERROR:
2524 switch(status) {
2525 case SENSOR_STATUS_NO_SERVICE:
2526 snprintf( buffer, sizeof(buffer), "KO: No sensor service found!\r\n" );
2527 break;
2528 case SENSOR_STATUS_DISABLED:
2529 snprintf( buffer, sizeof(buffer), "KO: '%s' sensor is disabled.\r\n", sensor );
2530 break;
2531 case SENSOR_STATUS_UNKNOWN:
2532 snprintf( buffer, sizeof(buffer),
2533 "KO: unknown sensor name: %s, run 'sensor status' to get available sensors.\r\n", sensor );
2534 break;
2535 default:
2536 snprintf( buffer, sizeof(buffer), "KO: '%s' sensor: exception happens.\r\n", sensor );
2537 }
2538 do_control_write( client, buffer );
2539 free( args_dup );
2540 return -1;
2541
2542INPUT_ERROR:
2543 control_write( client, "KO: Usage: \"set <sensorname> <value-a>[:<value-b>[:<value-c>]]\"\n" );
2544 free( args_dup );
2545 return -1;
2546}
2547
2548/* get all available sensor names and enable status respectively. */
2549static int
2550do_sensors_status( ControlClient client, char* args )
2551{
2552 uint8_t id, status;
2553 char buffer[SENSORS_INFO_SIZE] = { 0 };
2554
2555 for(id = 0; id < MAX_SENSORS; id++) {
2556 status = android_sensors_get_sensor_status( id );
2557 snprintf( buffer, sizeof(buffer), "%s: %s\n",
2558 android_sensors_get_name_from_id(id), (status ? "enabled.":"disabled.") );
2559 control_write( client, buffer );
2560 }
2561
2562 return 0;
2563}
2564
2565/* Sensor commands for get/set sensor values and get available sensor names. */
2566static const CommandDefRec sensor_commands[] =
2567{
2568 { "status", "list all sensors and their status.",
2569 "'status': list all sensors and their status.\r\n",
2570 NULL, do_sensors_status, NULL },
2571
2572 { "get", "get sensor values",
2573 "'get <sensorname>' returns the values of a given sensor.\r\n",
2574 NULL, do_sensors_get, NULL },
2575
2576 { "set", "set sensor values",
2577 "'set <sensorname> <value-a>[:<value-b>[:<value-c>]]' set the values of a given sensor.\r\n",
2578 NULL, do_sensors_set, NULL },
2579
2580 { NULL, NULL, NULL, NULL, NULL, NULL }
2581};
2582
2583/********************************************************************************************/
2584/********************************************************************************************/
2585/***** ******/
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002586/***** M A I N C O M M A N D S ******/
2587/***** ******/
2588/********************************************************************************************/
2589/********************************************************************************************/
2590
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002591static int
2592do_window_scale( ControlClient client, char* args )
2593{
2594 double scale;
2595 int is_dpi = 0;
2596 char* end;
2597
2598 if (!args) {
2599 control_write( client, "KO: argument missing, try 'window scale <scale>'\r\n" );
2600 return -1;
2601 }
2602
2603 scale = strtol( args, &end, 10 );
2604 if (end > args && !memcmp( end, "dpi", 4 )) {
2605 is_dpi = 1;
2606 }
2607 else {
2608 scale = strtod( args, &end );
2609 if (end == args || end[0]) {
2610 control_write( client, "KO: argument <scale> must be a real number, or an integer followed by 'dpi'\r\n" );
2611 return -1;
2612 }
2613 }
2614
David 'Digit' Turner496168e2014-07-12 05:28:13 +02002615 android_emulator_set_window_scale(scale, is_dpi);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002616 return 0;
2617}
2618
2619static const CommandDefRec window_commands[] =
2620{
2621 { "scale", "change the window scale",
2622 "'window scale <scale>' allows you to change the scale of the emulator window at runtime\r\n"
2623 "<scale> must be either a real number between 0.1 and 3.0, or an integer followed by\r\n"
2624 "the 'dpi' prefix (as in '120dpi')\r\n",
2625 NULL, do_window_scale, NULL },
2626
2627 { NULL, NULL, NULL, NULL, NULL, NULL }
2628};
2629
2630/********************************************************************************************/
2631/********************************************************************************************/
2632/***** ******/
David 'Digit' Turnere92bc562010-09-07 06:21:25 -07002633/***** Q E M U C O M M A N D S ******/
2634/***** ******/
2635/********************************************************************************************/
2636/********************************************************************************************/
2637
2638static int
2639do_qemu_monitor( ControlClient client, char* args )
2640{
David 'Digit' Turneraa1180c2014-01-28 06:08:00 +01002641 control_write(client, "KO: QEMU support no longer available\r\n");
2642 return -1;
David 'Digit' Turnere92bc562010-09-07 06:21:25 -07002643}
2644
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002645#ifdef CONFIG_STANDALONE_CORE
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002646/* UI settings, passed to the core via -ui-settings command line parameter. */
2647extern char* android_op_ui_settings;
2648
2649static int
2650do_attach_ui( ControlClient client, char* args )
2651{
2652 // Make sure that there are no UI already attached to this console.
2653 if (attached_ui_client != NULL) {
2654 control_write( client, "KO: Another UI is attached to this core!\r\n" );
2655 control_client_destroy(client);
2656 return -1;
2657 }
2658
Vladimir Chtchetkine85276802011-01-31 15:18:45 -08002659 if (!attachUiProxy_create(client->sock)) {
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002660 char reply_buf[4096];
Vladimir Chtchetkine85276802011-01-31 15:18:45 -08002661 attached_ui_client = client;
2662 // Reply "OK" with the saved -ui-settings property.
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002663 snprintf(reply_buf, sizeof(reply_buf), "OK: %s\r\n", android_op_ui_settings);
2664 control_write( client, reply_buf);
2665 } else {
Vladimir Chtchetkine85276802011-01-31 15:18:45 -08002666 control_write( client, "KO\r\n" );
2667 control_client_destroy(client);
2668 return -1;
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002669 }
2670
2671 return 0;
2672}
2673
Vladimir Chtchetkine85276802011-01-31 15:18:45 -08002674void
2675destroy_attach_ui_client(void)
2676{
2677 if (attached_ui_client != NULL) {
2678 control_client_destroy(attached_ui_client);
2679 }
2680}
2681
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002682static int
2683do_create_framebuffer_service( ControlClient client, char* args )
2684{
Vladimir Chtchetkine94a2fba2011-01-31 10:49:06 -08002685 ProxyFramebuffer* core_fb;
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002686 const char* protocol = "-raw"; // Default framebuffer exchange protocol.
David 'Digit' Turner7a5ee572011-02-02 15:58:45 +01002687 char reply_buf[64];
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002688
2689 // Protocol type is defined by the arguments passed with the stream switch
2690 // command.
2691 if (args != NULL && *args != '\0') {
2692 size_t token_len;
2693 const char* param_end = strchr(args, ' ');
2694 if (param_end == NULL) {
2695 param_end = args + strlen(args);
2696 }
2697 token_len = param_end - args;
2698 protocol = args;
2699
2700 // Make sure that this is one of the supported protocols.
2701 if (strncmp(protocol, "-raw", token_len) &&
2702 strncmp(protocol, "-shared", token_len)) {
2703 derror("Invalid framebuffer parameter %s\n", protocol);
2704 control_write( client, "KO: Invalid parameter\r\n" );
2705 control_client_destroy(client);
2706 return -1;
2707 }
2708 }
2709
David 'Digit' Turner7a5ee572011-02-02 15:58:45 +01002710 core_fb = proxyFb_create(client->sock, protocol);
2711 if (core_fb == NULL) {
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002712 control_write( client, "KO\r\n" );
2713 control_client_destroy(client);
2714 return -1;
2715 }
2716
David 'Digit' Turner7a5ee572011-02-02 15:58:45 +01002717 // Reply "OK" with the framebuffer's bits per pixel
2718 snprintf(reply_buf, sizeof(reply_buf), "OK: -bitsperpixel=%d\r\n",
2719 proxyFb_get_bits_per_pixel(core_fb));
2720 control_write( client, reply_buf);
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002721 return 0;
2722}
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -08002723
2724static int
2725do_create_user_events_service( ControlClient client, char* args )
2726{
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002727 // Make sure that there are no user events client already existing.
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -08002728 if (user_events_client != NULL) {
2729 control_write( client, "KO: Another user events service is already existing!\r\n" );
2730 control_client_destroy(client);
2731 return -1;
2732 }
2733
Vladimir Chtchetkine250b2e02011-01-28 10:56:16 -08002734 if (!userEventsImpl_create(client->sock)) {
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -08002735 char reply_buf[4096];
2736 user_events_client = client;
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -08002737 snprintf(reply_buf, sizeof(reply_buf), "OK\r\n");
2738 control_write( client, reply_buf);
2739 } else {
2740 control_write( client, "KO\r\n" );
2741 control_client_destroy(client);
2742 return -1;
2743 }
2744
2745 return 0;
2746}
Vladimir Chtchetkine6ee1c4e2011-01-20 11:22:32 -08002747
2748void
Vladimir Chtchetkine250b2e02011-01-28 10:56:16 -08002749destroy_user_events_client(void)
Vladimir Chtchetkine6ee1c4e2011-01-20 11:22:32 -08002750{
2751 if (user_events_client != NULL) {
2752 control_client_destroy(user_events_client);
2753 }
2754}
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002755
2756static int
2757do_create_ui_core_ctl_service( ControlClient client, char* args )
2758{
2759 // Make sure that there are no ui control client already existing.
2760 if (ui_core_ctl_client != NULL) {
2761 control_write( client, "KO: Another UI control service is already existing!\r\n" );
2762 control_client_destroy(client);
2763 return -1;
2764 }
2765
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -08002766 if (!coreCmdImpl_create(client->sock)) {
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002767 char reply_buf[4096];
2768 ui_core_ctl_client = client;
2769 snprintf(reply_buf, sizeof(reply_buf), "OK\r\n");
2770 control_write( client, reply_buf);
2771 } else {
2772 control_write( client, "KO\r\n" );
2773 control_client_destroy(client);
2774 return -1;
2775 }
2776
2777 return 0;
2778}
2779
2780void
2781destroy_ui_core_ctl_client(void)
2782{
2783 if (ui_core_ctl_client != NULL) {
2784 control_client_destroy(ui_core_ctl_client);
2785 }
2786}
2787
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -08002788void
2789destroy_corecmd_client(void)
2790{
2791 if (ui_core_ctl_client != NULL) {
2792 control_client_destroy(ui_core_ctl_client);
2793 }
2794}
2795
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002796static int
2797do_create_core_ui_ctl_service( ControlClient client, char* args )
2798{
2799 // Make sure that there are no ui control client already existing.
2800 if (core_ui_ctl_client != NULL) {
2801 control_write( client, "KO: Another UI control service is already existing!\r\n" );
2802 control_client_destroy(client);
2803 return -1;
2804 }
2805
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -08002806 if (!uiCmdProxy_create(client->sock)) {
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002807 char reply_buf[4096];
2808 core_ui_ctl_client = client;
2809 snprintf(reply_buf, sizeof(reply_buf), "OK\r\n");
2810 control_write( client, reply_buf);
2811 } else {
2812 control_write( client, "KO\r\n" );
2813 control_client_destroy(client);
2814 return -1;
2815 }
2816
2817 return 0;
2818}
2819
2820void
2821destroy_core_ui_ctl_client(void)
2822{
2823 if (core_ui_ctl_client != NULL) {
2824 control_client_destroy(core_ui_ctl_client);
2825 }
2826}
Vladimir Chtchetkine777eb682011-01-26 11:19:19 -08002827
2828void
2829destroy_uicmd_client(void)
2830{
2831 if (core_ui_ctl_client != NULL) {
2832 control_client_destroy(core_ui_ctl_client);
2833 }
2834}
2835
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002836#endif // CONFIG_STANDALONE_CORE
2837
David 'Digit' Turnere92bc562010-09-07 06:21:25 -07002838static const CommandDefRec qemu_commands[] =
2839{
2840 { "monitor", "enter QEMU monitor",
2841 "Enter the QEMU virtual machine monitor\r\n",
2842 NULL, do_qemu_monitor, NULL },
2843
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002844#ifdef CONFIG_STANDALONE_CORE
Vladimir Chtchetkinea473d812011-01-26 08:53:05 -08002845 { "attach-UI", "attach UI to the core",
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002846 "Attach UI to the core\r\n",
2847 NULL, do_attach_ui, NULL },
2848
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002849 { "framebuffer", "create framebuffer service",
2850 "Create framebuffer service\r\n",
2851 NULL, do_create_framebuffer_service, NULL },
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -08002852
Vladimir Chtchetkinea473d812011-01-26 08:53:05 -08002853 { "user-events", "create user events service",
Vladimir Chtchetkine9411a562011-01-19 18:29:27 -08002854 "Create user events service\r\n",
2855 NULL, do_create_user_events_service, NULL },
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002856
Vladimir Chtchetkinea473d812011-01-26 08:53:05 -08002857 { "ui-core-control", "create UI control service",
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002858 "Create UI control service\r\n",
2859 NULL, do_create_ui_core_ctl_service, NULL },
2860
Vladimir Chtchetkinea473d812011-01-26 08:53:05 -08002861 { "core-ui-control", "create UI control service",
Vladimir Chtchetkine6b985d72011-01-20 18:02:35 -08002862 "Create UI control service\r\n",
2863 NULL, do_create_core_ui_ctl_service, NULL },
Vladimir Chtchetkinee95660a2010-12-20 08:28:03 -08002864#endif // CONFIG_STANDALONE_CORE
2865
David 'Digit' Turnere92bc562010-09-07 06:21:25 -07002866 { NULL, NULL, NULL, NULL, NULL, NULL }
2867};
2868
2869
2870/********************************************************************************************/
2871/********************************************************************************************/
2872/***** ******/
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002873/***** M A I N C O M M A N D S ******/
2874/***** ******/
2875/********************************************************************************************/
2876/********************************************************************************************/
2877
2878static int
2879do_kill( ControlClient client, char* args )
2880{
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002881 control_write( client, "OK: killing emulator, bye bye\r\n" );
2882 exit(0);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002883}
2884
2885static const CommandDefRec main_commands[] =
2886{
2887 { "help|h|?", "print a list of commands", NULL, NULL, do_help, NULL },
2888
2889 { "event", "simulate hardware events",
2890 "allows you to send fake hardware events to the kernel\r\n", NULL,
2891 NULL, event_commands },
2892
2893 { "geo", "Geo-location commands",
2894 "allows you to change Geo-related settings, or to send GPS NMEA sentences\r\n", NULL,
2895 NULL, geo_commands },
2896
2897 { "gsm", "GSM related commands",
2898 "allows you to change GSM-related settings, or to make a new inbound phone call\r\n", NULL,
2899 NULL, gsm_commands },
2900
Jaime Lopez1a000852010-07-21 18:03:58 -07002901 { "cdma", "CDMA related commands",
2902 "allows you to change CDMA-related settings\r\n", NULL,
2903 NULL, cdma_commands },
2904
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002905 { "kill", "kill the emulator instance", NULL, NULL,
Vladimir Chtchetkined87b0802010-12-06 10:55:11 -08002906 do_kill, NULL },
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002907
2908 { "network", "manage network settings",
2909 "allows you to manage the settings related to the network data connection of the\r\n"
2910 "emulated device.\r\n", NULL,
2911 NULL, network_commands },
2912
2913 { "power", "power related commands",
2914 "allows to change battery and AC power status\r\n", NULL,
2915 NULL, power_commands },
2916
2917 { "quit|exit", "quit control session", NULL, NULL,
2918 do_quit, NULL },
2919
2920 { "redir", "manage port redirections",
2921 "allows you to add, list and remove UDP and/or PORT redirection from the host to the device\r\n"
2922 "as an example, 'redir tcp:5000:6000' will route any packet sent to the host's TCP port 5000\r\n"
2923 "to TCP port 6000 of the emulated device\r\n", NULL,
2924 NULL, redir_commands },
2925
2926 { "sms", "SMS related commands",
2927 "allows you to simulate an inbound SMS\r\n", NULL,
2928 NULL, sms_commands },
2929
Ot ten Thije2ff39a32010-10-06 17:48:15 +01002930 { "avd", "control virtual device execution",
2931 "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 -08002932 NULL, vm_commands },
2933
2934 { "window", "manage emulator window",
2935 "allows you to modify the emulator window\r\n", NULL,
2936 NULL, window_commands },
2937
David 'Digit' Turnere92bc562010-09-07 06:21:25 -07002938 { "qemu", "QEMU-specific commands",
2939 "allows to connect to the QEMU virtual machine monitor\r\n", NULL,
2940 NULL, qemu_commands },
2941
Tim Wan736e01f2011-01-10 10:58:25 +01002942 { "sensor", "manage emulator sensors",
2943 "allows you to request the emulator sensors\r\n", NULL,
2944 NULL, sensor_commands },
2945
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08002946 { NULL, NULL, NULL, NULL, NULL, NULL }
2947};
2948
2949
2950static ControlGlobalRec _g_global;
2951
2952int
2953control_console_start( int port )
2954{
2955 return control_global_init( &_g_global, port );
2956}