diff options
author | David Turner <> | 2009-04-12 14:59:50 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-04-12 14:59:50 -0700 |
commit | 3a0c4d9eed9ba76e2744258af212b8c1269a24a5 (patch) | |
tree | b9bf7585de87708a175f1753dc455b97cc434581 | |
parent | 850869d21220b9d5d14c45dd39e253f5a6f43080 (diff) | |
download | external_qemu-3a0c4d9eed9ba76e2744258af212b8c1269a24a5.zip external_qemu-3a0c4d9eed9ba76e2744258af212b8c1269a24a5.tar.gz external_qemu-3a0c4d9eed9ba76e2744258af212b8c1269a24a5.tar.bz2 |
AI 145806: Add qemud-version auto-detection logic to android/hw-qemud.c
Document the supported QEMUD services in known Android systems
Print "Press F6 to exit trackball mode..." in the window title bar when one
activates the persistent trackball mode. The actual text depends on your
key binding configuration.
Refine the skin directory auto-search paths when an AVD name is not given on the
command line. This makes the following work correctly:
tools/emulator -sysdir platforms/android-1.5/images -data foo.img
BUG=1745505
Automated import of CL 145806
-rw-r--r-- | CHANGES.TXT | 8 | ||||
-rw-r--r-- | android/avd/info.c | 15 | ||||
-rw-r--r-- | android/hw-qemud.c | 282 | ||||
-rw-r--r-- | android/main.c | 35 | ||||
-rw-r--r-- | docs/ANDROID-QEMUD-SERVICES.TXT | 155 | ||||
-rw-r--r-- | docs/ANDROID-QEMUD.TXT | 18 |
6 files changed, 418 insertions, 95 deletions
diff --git a/CHANGES.TXT b/CHANGES.TXT index 93d3689..d3c301d 100644 --- a/CHANGES.TXT +++ b/CHANGES.TXT @@ -121,6 +121,14 @@ OTHER: - Using '-no-audio' no longer disables sound hardware emulation. It simply mutes the emulator program on the host. +- The window title bar changes when you toggle persistent trackball mode + (F6 by default). It will display something like the following: + + "Press F6 to exit trackball mode ..." + + The actual text depends on your key binding configuration. This is to help + people toggle the mode by accident. + ============================================================================== Changes between 1.6 and 1.7 diff --git a/android/avd/info.c b/android/avd/info.c index ec74249..1bdb59c 100644 --- a/android/avd/info.c +++ b/android/avd/info.c @@ -1281,9 +1281,18 @@ _getBuildSkin( AvdInfo* i, AvdInfoParams* params ) #define PREBUILT_SKINS_DIR "development/emulator/skins" - /* the (current) default skin directory */ - p = bufprint( temp, end, "%s/%s", - i->androidBuildRoot, PREBUILT_SKINS_DIR ); + do { + /* try in <sysdir>/../skins first */ + p = bufprint( temp, end, "%s/../skins", + i->androidBuildRoot ); + if (path_exists(temp)) + break; + + /* the (current) default skin directory */ + p = bufprint( temp, end, "%s/%s", + i->androidBuildRoot, PREBUILT_SKINS_DIR ); + } while (0); + } else { p = bufprint( temp, end, "%s", skinDir ); } diff --git a/android/hw-qemud.c b/android/hw-qemud.c index 25d8717..cc28e63 100644 --- a/android/hw-qemud.c +++ b/android/hw-qemud.c @@ -40,93 +40,19 @@ #define MAX_FRAME_PAYLOAD 65535 +/* define SUPPORT_LEGACY_QEMUD to 1 if you want to support + * talking to a legacy qemud daemon. See docs/ANDROID-QEMUD.TXT + * for details. + */ +#define SUPPORT_LEGACY_QEMUD 1 + /* - * the qemud daemon program is only used within Android as a bridge - * between the emulator program and the emulated system. it really works as - * a simple stream multiplexer that works as follows: - * - * - qemud is started by init following instructions in - * /system/etc/init.goldfish.rc (i.e. it is never started on real devices) - * - * - qemud communicates with the emulator program through a single serial - * port, whose name is passed through a kernel boot parameter - * (e.g. android.qemud=ttyS1) - * - * - qemud binds one unix local stream socket (/dev/socket/qemud, created - * by init through /system/etc/init.goldfish.rc). - * - * - * emulator <==serial==> qemud <---> /dev/socket/qemud <-+--> client1 - * | - * +--> client2 - * - * - the protocol used on the serial connection is pretty simple: - * - * offset size description - * 0 2 2-char hex string giving the destination or - * source channel - * 2 4 4-char hex string giving the payload size - * 6 n the message payload - * - * for emulator->system messages, the 'channel' index indicates - * to which channel the payload must be sent - * - * for system->emulator messages, the 'channel' index indicates from - * which channel the payload comes from. - * - * the payload size is limited to MAX_SERIAL_PAYLOAD bytes. - * - * - communication between a client and qemud is stream based, but supports - * an optional framing mode which can be used to send packets larger than - * MAX_SERIAL_PAYLOAD bytes and provides packet bounding. - * - * offset size description - * 0 4 4-char hex string giving the payload size - * 6 n the message payload - * - * The size of the payload is then limited to 65535 bytes. - * - * - the special channel index 0 is used by the emulator and qemud only. - * other channel numbers correspond to clients. More specifically, - * connection are created like this: - * - * * the client connects to /dev/socket/qemud - * - * * the client sends the service name through the socket, as - * <service-name> - * - * * qemud creates a "Client" object internally, assigns it an - * internal unique channel number > 0, then sends a connection - * initiation request to the emulator (i.e. through channel 0): - * - * connect:<id>:<name> + * This implements support for the 'qemud' multiplexing communication + * channel between clients running in the emulated system and 'services' + * provided by the emulator. * - * where <name> is the service name, and <id> is a 2-hexchar - * number corresponding to the channel number. + * For additional details, please read docs/ANDROID-QEMUD.TXT * - * * in case of success, the emulator responds through channel 0 - * with: - * - * ok:connect:<id> - * - * after this, all messages between the client and the emulator - * are passed in pass-through mode. If the client closes the - * connection, qemud sends the following to the emulator: - * - * disconnect:<id> - * - * * if the emulator refuses the service connection, it will - * send the following through channel 0: - * - * ko:connect:<id>:reason-for-failure - * - * * any command sent through channel 0 to the emulator that is - * not properly recognized will be answered by: - * - * ko:unknown command - * - * Internally, the daemon maintains a "Client" object for each client - * connection (i.e. accepting socket connection). */ /* @@ -154,12 +80,18 @@ /** HANDLING INCOMING DATA FRAMES **/ +/* A QemudSink is just a handly data structure that is used to + * read a fixed amount of bytes into a buffer + */ typedef struct QemudSink { int len; int size; uint8_t* buff; } QemudSink; +/* reset a QemudSink, i.e. provide a new destination buffer address + * and its size in bytes. + */ static void qemud_sink_reset( QemudSink* ss, int size, uint8_t* buffer ) { @@ -168,6 +100,12 @@ qemud_sink_reset( QemudSink* ss, int size, uint8_t* buffer ) ss->buff = buffer; } +/* try to fill the sink by reading bytes from the source buffer + * '*pmsg' which contains '*plen' bytes + * + * this functions updates '*pmsg' and '*plen', and returns + * 1 if the sink's destination buffer is full, or 0 otherwise. + */ static int qemud_sink_fill( QemudSink* ss, const uint8_t* *pmsg, int *plen) { @@ -187,6 +125,9 @@ qemud_sink_fill( QemudSink* ss, const uint8_t* *pmsg, int *plen) return (ss->len == ss->size); } +/* returns the number of bytes needed to fill a sink's destination + * buffer. + */ static int qemud_sink_needed( QemudSink* ss ) { @@ -215,6 +156,17 @@ qemud_sink_needed( QemudSink* ss ) #define CHANNEL_OFFSET 0 #define CHANNEL_SIZE 2 +#if SUPPORT_LEGACY_QEMUD +typedef enum { + QEMUD_VERSION_UNKNOWN, + QEMUD_VERSION_LEGACY, + QEMUD_VERSION_NORMAL +} QemudVersion; + +# define LEGACY_LENGTH_OFFSET 0 +# define LEGACY_CHANNEL_OFFSET 4 +#endif + /* length of the framed header */ #define FRAME_HEADER_SIZE 4 @@ -233,6 +185,9 @@ typedef struct QemudSerial { int overflow; int in_size; int in_channel; +#if SUPPORT_LEGACY_QEMUD + QemudVersion version; +#endif QemudSink header[1]; QemudSink payload[1]; uint8_t data0[MAX_SERIAL_PAYLOAD+1]; @@ -293,9 +248,33 @@ qemud_serial_read( void* opaque, const uint8_t* from, int len ) if (!qemud_sink_fill(s->header, (const uint8_t**)&from, &len)) break; +#if SUPPORT_LEGACY_QEMUD + if (s->version == QEMUD_VERSION_UNKNOWN) { + /* if we receive "001200" as the first header, then we + * detected a legacy qemud daemon. See the comments + * in qemud_serial_send_legacy_probe() for details. + */ + if ( !memcmp(s->data0, "001200", 6) ) { + D("%s: legacy qemud detected.", __FUNCTION__); + s->version = QEMUD_VERSION_LEGACY; + } else { + D("%s: normal qemud detected.", __FUNCTION__); + s->version = QEMUD_VERSION_NORMAL; + } + } + + if (s->version == QEMUD_VERSION_LEGACY) { + s->in_size = hex2int( s->data0 + LEGACY_LENGTH_OFFSET, LENGTH_SIZE ); + s->in_channel = hex2int( s->data0 + LEGACY_CHANNEL_OFFSET, CHANNEL_SIZE ); + } else { + s->in_size = hex2int( s->data0 + LENGTH_OFFSET, LENGTH_SIZE ); + s->in_channel = hex2int( s->data0 + CHANNEL_OFFSET, CHANNEL_SIZE ); + } +#else /* extract payload length + channel id */ s->in_size = hex2int( s->data0 + LENGTH_OFFSET, LENGTH_SIZE ); s->in_channel = hex2int( s->data0 + CHANNEL_OFFSET, CHANNEL_SIZE ); +#endif s->header->len = 0; if (s->in_size <= 0 || s->in_channel < 0) { @@ -332,6 +311,68 @@ qemud_serial_read( void* opaque, const uint8_t* from, int len ) } } + +#if SUPPORT_LEGACY_QEMUD +static void +qemud_serial_send_legacy_probe( QemudSerial* s ) +{ + /* we're going to send a specially crafted packet to the qemud + * daemon, this will help us determine whether we're talking + * to a legacy or a normal daemon. + * + * the trick is to known that a legacy daemon uses the following + * header: + * + * <length><channel><payload> + * + * while the normal one uses: + * + * <channel><length><payload> + * + * where <channel> is a 2-hexchar string, and <length> a 4-hexchar + * string. + * + * if we send a header of "000100", it is interpreted: + * + * - as the header of a 1-byte payload by the legacy daemon + * - as the header of a 256-byte payload by the normal one. + * + * we're going to send something that looks like: + * + * "000100" + "X" + + * "000b00" + "connect:gsm" + + * "000b00" + "connect:gps" + + * "000f00" + "connect:control" + + * "00c210" + "0"*194 + * + * the normal daemon will interpret this as a 256-byte payload + * for channel 0, with garbage content ("X000b00conn...") which + * will be silently ignored. + * + * on the other hand, the legacy daemon will see it as a + * series of packets: + * + * one message "X" on channel 0, which will force the daemon + * to send back "001200ko:unknown command" as its first answer. + * + * three "connect:<xxx>" messages used to receive the channel + * numbers of the three legacy services implemented by the daemon. + * + * a garbage packet of 194 zeroes for channel 16, which will be + * silently ignored. + */ + uint8_t tab[194]; + + memset(tab, 0, sizeof(tab)); + qemu_chr_write(s->cs, (uint8_t*)"000100X", 7); + qemu_chr_write(s->cs, (uint8_t*)"000b00connect:gsm", 17); + qemu_chr_write(s->cs, (uint8_t*)"000b00connect:gps", 17); + qemu_chr_write(s->cs, (uint8_t*)"000f00connect:control", 21); + qemu_chr_write(s->cs, (uint8_t*)"00c210", 6); + qemu_chr_write(s->cs, tab, sizeof(tab)); +} +#endif /* SUPPORT_LEGACY_QEMUD */ + /* intialize a QemudSerial object with a charpipe endpoint * and a receiver. */ @@ -351,6 +392,11 @@ qemud_serial_init( QemudSerial* s, s->in_size = 0; s->in_channel = -1; +#if SUPPORT_LEGACY_QEMUD + s->version = QEMUD_VERSION_UNKNOWN; + qemud_serial_send_legacy_probe(s); +#endif + qemu_chr_add_handlers( cs, qemud_serial_can_read, qemud_serial_read, @@ -391,13 +437,25 @@ qemud_serial_send( QemudSerial* s, avail = MAX_SERIAL_PAYLOAD; /* write this packet's header */ +#if SUPPORT_LEGACY_QEMUD + if (s->version == QEMUD_VERSION_LEGACY) { + int2hex(header + LEGACY_LENGTH_OFFSET, LENGTH_SIZE, avail); + int2hex(header + LEGACY_CHANNEL_OFFSET, CHANNEL_SIZE, channel); + } else { + int2hex(header + LENGTH_OFFSET, LENGTH_SIZE, avail); + int2hex(header + CHANNEL_OFFSET, CHANNEL_SIZE, channel); + } +#else int2hex(header + LENGTH_OFFSET, LENGTH_SIZE, avail); int2hex(header + CHANNEL_OFFSET, CHANNEL_SIZE, channel); +#endif + T("%s: '%.*s'", __FUNCTION__, HEADER_SIZE, header); qemu_chr_write(s->cs, header, HEADER_SIZE); /* insert frame header when needed */ if (framing) { int2hex(frame, FRAME_HEADER_SIZE, msglen); + T("%s: '%.*s'", __FUNCTION__, FRAME_HEADER_SIZE, frame); qemu_chr_write(s->cs, frame, FRAME_HEADER_SIZE); avail -= FRAME_HEADER_SIZE; len -= FRAME_HEADER_SIZE; @@ -405,6 +463,7 @@ qemud_serial_send( QemudSerial* s, } /* write message content */ + T("%s: '%.*s'", __FUNCTION__, avail, msg); qemu_chr_write(s->cs, msg, avail); msg += avail; len -= avail; @@ -894,6 +953,61 @@ qemud_multiplexer_control_recv( void* opaque, return; } +#if SUPPORT_LEGACY_QEMUD + /* an ok:connect:<service>:<id> message can be received if we're + * talking to a legacy qemud daemon, i.e. one running in a 1.0 or + * 1.1 system image. + * + * we should treat is as a normal "connect:" attempt, except that + * we must not send back any acknowledgment. + */ + if (msglen > 11 && !memcmp(msg, "ok:connect:", 11)) { + const char* service_name = (const char*)msg + 11; + char* q = strchr(service_name, ':'); + int channel; + + if (q == NULL || q+3 != (char*)msgend) { + D("%s: malformed legacy connect message: '%.*s' (offset=%d)", + __FUNCTION__, msglen, (const char*)msg, q ? q-(char*)msg : -1); + return; + } + *q++ = 0; /* zero-terminate service name */ + channel = hex2int((uint8_t*)q, 2); + if (channel <= 0) { + D("%s: malformed legacy channel id '%.*s", + __FUNCTION__, 2, q); + return; + } + + switch (mult->serial->version) { + case QEMUD_VERSION_UNKNOWN: + mult->serial->version = QEMUD_VERSION_LEGACY; + D("%s: legacy qemud daemon detected.", __FUNCTION__); + break; + + case QEMUD_VERSION_LEGACY: + /* nothing unusual */ + break; + + default: + D("%s: weird, ignoring legacy qemud control message: '%.*s'", + __FUNCTION__, msglen, msg); + return; + } + + /* "hw-control" was called "control" in 1.0/1.1 */ + if (!strcmp(service_name,"control")) + service_name = "hw-control"; + + qemud_multiplexer_connect(mult, service_name, channel); + return; + } + + /* anything else, don't answer for legacy */ + if (mult->serial->version == QEMUD_VERSION_LEGACY) + return; +#endif /* SUPPORT_LEGACY_QEMUD */ + /* anything else is a problem */ p = bufprint(tmp, end, "ko:unknown command"); qemud_serial_send(mult->serial, 0, 0, (uint8_t*)tmp, p-tmp); diff --git a/android/main.c b/android/main.c index efa79aa..ed005d2 100644 --- a/android/main.c +++ b/android/main.c @@ -484,14 +484,40 @@ android_emulator_set_window_scale( double scale, int is_dpi ) static void qemulator_set_title( QEmulator* emulator ) { - char temp[64]; + char temp[128], *p=temp, *end=p+sizeof temp;; if (emulator->window == NULL) return; - snprintf( temp, sizeof(temp), "Android Emulator (%s:%d)", - avdInfo_getName( android_avdInfo ), - android_base_port ); + if (emulator->show_trackball) { + SkinKeyBinding bindings[ SKIN_KEY_COMMAND_MAX_BINDINGS ]; + int count; + + count = skin_keyset_get_bindings( android_keyset, + SKIN_KEY_COMMAND_TOGGLE_TRACKBALL, + bindings ); + + if (count > 0) { + int nn; + p = bufprint( p, end, "Press " ); + for (nn = 0; nn < count; nn++) { + if (nn > 0) { + if (nn < count-1) + p = bufprint(p, end, ", "); + else + p = bufprint(p, end, " or "); + } + p = bufprint(p, end, "%s", + skin_key_symmod_to_str( bindings[nn].sym, + bindings[nn].mod ) ); + } + p = bufprint(p, end, " to leave trackball mode. "); + } + } + + p = bufprint(p, end, "Android Emulator (%s:%d)", + avdInfo_getName( android_avdInfo ), + android_base_port ); skin_window_set_title( emulator->window, temp ); } @@ -778,6 +804,7 @@ handle_key_command( void* opaque, SkinKeyCommand command, int down ) case SKIN_KEY_COMMAND_TOGGLE_TRACKBALL: emulator->show_trackball = !emulator->show_trackball; skin_window_show_trackball( emulator->window, emulator->show_trackball ); + qemulator_set_title( emulator ); break; case SKIN_KEY_COMMAND_ONION_ALPHA_UP: diff --git a/docs/ANDROID-QEMUD-SERVICES.TXT b/docs/ANDROID-QEMUD-SERVICES.TXT new file mode 100644 index 0000000..24636e5 --- /dev/null +++ b/docs/ANDROID-QEMUD-SERVICES.TXT @@ -0,0 +1,155 @@ +ANDROID QEMUD SERVICES + +The docs/ANDROID-QEMUD.TXT document describes how clients running in the +emulated system can communicate with the emulator through the 'qemud' +multiplexing daemon. + +This document lists all services officially provided by the emulator to +clients. Each service is identified by a unique name. There is no provision +for versioning. Instead, if you need new features, create a new service +with a slightly different name and modify clients to use it in the system +image. + + +"gsm" service: +-------------- + + The GSM service provides a communication channel where GSM/GPRS AT commands + can be exchanged between the emulated system and an emulated modem. + + The messages consist in un-framed character messages as they appear in a + regular AT command channel (i.e. terminated by \r\n most of the time). + + There can be only 1 client talking to the modem, since the AT command + protocol does not allow for anything more complex. + + Implementation: telephony/modem_driver.c + Since: SDK 1.0 + +"gps" service: +-------------- + + The GPS service is used to broadcast NMEA 0183 sentences containing fix + information to all clients. The service doesn't listen to clients at all. + + All sentences are un-framed and end with a \n character. + + Implementation: android/gps.c + Since: SDK 1.0 + + +"hw-control" / "control" service: +--------------------- + + This service is named "control" in 1.0 and 1.1, and "hw-control" + in 1.5 and later releases of the SDK. + + This service is used to allow the emulated system to control various aspects + of hardware emulation. The corresponding clients are in + hardware/libhardware_legacy in the Android source tree. + + All messages use the optional framing described in ANDROID-QEMUD.TXT. + Only one client can talk with the service at any one time, but clients + quickly connect/disconnect to the service anyway. + + Supported messages are: + + 1/ Client sends "power:light:brightness:<lightname>:<val>", where + <lightname> is the name of a light and <val> is an decimal value + between 0 (off) and 255 (brightest). Valid values for 'light' are: + + lcd_backlight + keyboard_backlight + button_backlight + + Currently, only 'lcd_backlight' is supported by the emulator. + Note that the brightness value doesn't scale linearly on physical + devices. + + 2/ Client sends "set_led_state:<color-rgb>:<on-ms>:<off-ms>" where + <color-rgb> is an 8-hexchar value describing an xRGB color, and + <on-ms> and <off-ms> are decimal values expressing timeout in + milliseconds. + + This is used to modify the color of the notification LED on the + emulated phone as well as provide on/off timeouts for flashing. + + cCrrently unsupported by the emulator. + + 3/ Client sends "vibrator:<timeout>", where <timeout> is a decimal value. + used to enable vibrator emulation for <timeout> milli-seconds. + + Currently unsupported by the emulator. + + + Implementation: android/hw-control.c + Since: SDK 1.0 + + +"sensors" service: +------------------ + + This service is used for sensor emulation. All messages are framed and the + protocol is the following: + + 1/ Clients initially sends "list-sensors" and receives a single string + containing a decimal mask value. The value is a set of bit-flags + indicating which sensors are provided by the hardware emulation. The + bit flags are defined as: + + bit 0: accelerometer + bit 1: compass + bit 2: orientation + bit 3: temperature + + 2/ Client sends "set-delay:<delay-ms>", where <delay-ms> is a decimal + string, to set the minimum delay in milliseconds between sensor event + reports it wants to receive. + + 3/ Client sends "wake", the service must immediately send back "wake" as + an answer. This is used to simplify parts of the client emulation. + + 4/ Client sends "set:<sensor-name>:<flag>", where <sensor-name> is the + name of one of the emulated sensors, and <flag> is either "0" or "1". + This is used to enable or disable the report of data from a given + emulated sensor hardware. + + the list of valid <sensor-name> values is the following: + + acceleration : for the accelerometer + magnetic-field : for the compass + orientation : for the orientation sensor + temperature : for the temperature sensor + + + 5/ If at least one of the emulated sensors has been enabled with + "set:<name>:1", then the service should broadcast periodically the + state of sensors. + + this is done by broadcasting a series of framed messages that look + like: + + acceleration:<x>:<y>:<z> + magnetic-field:<x>:<y>:<z> + orientation:<azimuth>:<pitch>:<roll> + temperature:<celsius> + sync:<time_us> + + Note that each line, corresponds to an independent framed message. + Each line, except the last one, is optional and should only be + produced if the corresponding sensor reporting has been enabled + through "set:<name>:1". + + <x>, <y>, <z>, <azimuth>, <pitch>, <roll> and <celsius> are floating + point values written as strings with the "%g" printf formatting option. + + The last 'sync' message is required to end the broadcast and contains + the current VM clock time in micro-seconds. The client will adjust it + internally to only care about differences between sensor broadcasts. + + If reporting is disabled for all sensors, no broadcast message needs + to be sent back to clients. + + + Implementation: android/hw-sensors.c + Since: SDK 1.5 (cupcake) diff --git a/docs/ANDROID-QEMUD.TXT b/docs/ANDROID-QEMUD.TXT index 6415a47..1364553 100644 --- a/docs/ANDROID-QEMUD.TXT +++ b/docs/ANDROID-QEMUD.TXT @@ -92,7 +92,7 @@ Since the "cupcake" platform, this works as follows: connect:<service>:<id> - where <service> is the service name, and <id> is a 4-hexchar string + where <service> is the service name, and <id> is a 2-hexchar string giving the allocated channel index for the client. @@ -181,9 +181,19 @@ This is documented here since this explains some subtleties in the implementation code of android/hw-qemud.c The old scheme also used a serial port to allow the daemon and the emulator -to communicate. Besides, the same multiplexing protocol was used to pass -messages through multiple channels. However, several other differences -exist, best illustrated by the following graphics: +to communicate. However, the multiplexing protocol swaps the position of +'channel' and 'length' in the header: + + offset size description + + 0 4 4-char hex string giving the payload size + + 4 2 2-char hex string giving the destination or + source channel + + 6 n the message payload + +Several other differences, best illustrated by the following graphics: emulator <==serial==> qemud <-+--> /dev/socket/qemud_gsm <--> GSM client | |