diff options
Diffstat (limited to 'android')
-rw-r--r-- | android/avd/hardware-properties.ini | 7 | ||||
-rw-r--r-- | android/avd/hw-config-defs.h | 7 | ||||
-rw-r--r-- | android/avd/info.c | 13 | ||||
-rw-r--r-- | android/cmdline-options.h | 5 | ||||
-rw-r--r-- | android/help.c | 16 | ||||
-rw-r--r-- | android/hw-control.c | 147 | ||||
-rw-r--r-- | android/hw-qemud.c | 1194 | ||||
-rw-r--r-- | android/hw-qemud.h (renamed from android/qemud.h) | 36 | ||||
-rw-r--r-- | android/hw-sensors.c | 450 | ||||
-rw-r--r-- | android/hw-sensors.h | 48 | ||||
-rw-r--r-- | android/main.c | 40 | ||||
-rw-r--r-- | android/qemud.c | 456 | ||||
-rw-r--r-- | android/skin/window.c | 6 | ||||
-rw-r--r-- | android/utils/debug.h | 1 |
14 files changed, 1827 insertions, 599 deletions
diff --git a/android/avd/hardware-properties.ini b/android/avd/hardware-properties.ini index 3093b16..60a7493 100644 --- a/android/avd/hardware-properties.ini +++ b/android/avd/hardware-properties.ini @@ -96,6 +96,13 @@ default = yes abstract = Battery support description = Whether the device can run on a battery. +# Accelerometer (used for auto-rotation) +name = hw.accelerometer +type = boolean +default = yes +abstract = Accelerometer +description = Whether there is an accelerometer in the device. + # Audio input name = hw.audioInput type = boolean diff --git a/android/avd/hw-config-defs.h b/android/avd/hw-config-defs.h index fd85bf2..3e7cec6 100644 --- a/android/avd/hw-config-defs.h +++ b/android/avd/hw-config-defs.h @@ -95,6 +95,13 @@ HWCFG_BOOL( "Whether the device can run on a battery.") HWCFG_BOOL( + hw_accelerometer, + "hw.accelerometer", + "yes", + "Accelerometer", + "Whether there is an accelerometer in the device.") + +HWCFG_BOOL( hw_audioInput, "hw.audioInput", "yes", diff --git a/android/avd/info.c b/android/avd/info.c index afff342..11c5d19 100644 --- a/android/avd/info.c +++ b/android/avd/info.c @@ -1242,7 +1242,7 @@ _getBuildSkin( AvdInfo* i, AvdInfoParams* params ) if (!skinName) { /* the (current) default skin name for the build system */ skinName = SKIN_DEFAULT; - DD("selecting default skin name '%s'", skinName); + D("selecting default skin name '%s'", skinName); } i->skinName = ASTRDUP(skinName); @@ -1354,6 +1354,17 @@ avdInfo_getHwConfig( AvdInfo* i, AndroidHwConfig* hw ) if (ini != i->configIni) iniFile_free(ini); + /* special product-specific hardware configuration */ + if (i->androidOut != NULL) + { + char* p = strrchr(i->androidOut, '/'); + if (p != NULL && p[0] != 0) { + if (p[1] == 's') { + hw->hw_keyboard = 0; + } + } + } + return ret; } diff --git a/android/cmdline-options.h b/android/cmdline-options.h index c57ed10..5291177 100644 --- a/android/cmdline-options.h +++ b/android/cmdline-options.h @@ -16,7 +16,7 @@ #endif /* required to ensure that the CONFIG_XXX macros are properly defined */ -//XXX#include "config.h" +#include "android/config/config.h" /* Some options acts like flags, while others must be followed by a parameter * string. Nothing really new here. @@ -126,11 +126,12 @@ OPT_PARAM( shell_serial, "<device>", "specific character device for root shell" OPT_FLAG ( old_system, "support old (pre 1.4) system images" ) OPT_PARAM( tcpdump, "<file>", "capture network packets to file" ) +OPT_PARAM( bootchart, "<timeout>", "enable bootcharting") + #ifdef CONFIG_NAND_LIMITS OPT_PARAM( nand_limits, "<nlimits>", "enforce NAND/Flash read/write thresholds" ) #endif -OPT_PARAM( bootchart, "<timeout>", "enable bootcharting") #undef CFG_FLAG #undef CFG_PARAM diff --git a/android/help.c b/android/help.c index ae4dbc0..391b63d 100644 --- a/android/help.c +++ b/android/help.c @@ -35,8 +35,10 @@ help_virtual_device( stralloc_t* out ) " The 'android' helper tool can be used to manage virtual devices.\n" " For example:\n\n" - " android avd -- creates a new virtual device.\n" - " android list -- list all virtual devices available.\n\n" + " android create avd -n <name> -t 1 # creates a new virtual device.\n" + " android list avd # list all virtual devices available.\n\n" + + " Try 'android --help' for more commands.\n\n" " Each AVD really corresponds to a content directory which stores\n" " persistent and writable disk images as well as configuration files.\n" @@ -446,14 +448,14 @@ static void help_avd(stralloc_t* out) { PRINTF( - " use '-avd <name>' to start the emulator program with a given avd,\n" - " where <name> must correspond to the name of one of the\n" - " Android Virtual Devices available on your host.\n\n" + " use '-avd <name>' to start the emulator program with a given Android\n" + " Virtual Device (a.k.a. AVD), where <name> must correspond to the name\n" + " of one of the existing AVDs available on your host machine.\n\n" + + "See -help-virtual-device to learn how to create/list/manage AVDs.\n\n" " As a special convenience, using '@<name>' is equivalent to using\n" " '-avd <name>'.\n\n" - - " For more information about virtual devices, see -help-virtual-device.\n" ); } diff --git a/android/hw-control.c b/android/hw-control.c index 681e85b..a82a92a 100644 --- a/android/hw-control.c +++ b/android/hw-control.c @@ -22,7 +22,7 @@ */ #include "android/hw-control.h" #include "cbuffer.h" -#include "android/qemud.h" +#include "android/hw-qemud.h" #include "android/utils/misc.h" #include "android/utils/debug.h" #include "qemu-char.h" @@ -40,104 +40,37 @@ #define T(...) ((void)0) #endif -static void* hw_control_client; -static AndroidHwControlFuncs hw_control_funcs; - -#define BUFFER_SIZE 512 - typedef struct { - CharDriverState* cs; - int overflow; - int wanted; - CBuffer input[1]; - char input_0[ BUFFER_SIZE ]; - /* note: 1 more byte to zero-terminate the query */ - char query[ BUFFER_SIZE+1 ]; + void* client; + AndroidHwControlFuncs client_funcs; + QemudService* service; } HwControl; -/* forward */ -static void hw_control_do_query( HwControl* h, - uint8_t* query, - int querylen ); +/* handle query */ +static void hw_control_do_query( HwControl* h, uint8_t* query, int querylen ); +/* called when a qemud client sends a command */ static void -hw_control_init( HwControl* h, CharDriverState* cs ) +_hw_control_qemud_client_recv( void* opaque, + uint8_t* msg, + int msglen ) { - h->cs = cs; - h->overflow = 0; - h->wanted = 0; - cbuffer_reset( h->input, h->input_0, sizeof h->input_0 ); + hw_control_do_query(opaque, msg, msglen); } -static int -hw_control_can_read( void* _hw ) +/* called when a qemud client connects to the service */ +static QemudClient* +_hw_control_qemud_connect( void* opaque, QemudService* service, int channel ) { - HwControl* h = _hw; - return cbuffer_write_avail( h->input ); -} - -static void -hw_control_read( void* _hw, const uint8_t* data, int len ) -{ - HwControl* h = _hw; - CBuffer* input = h->input; - - T("%s: %4d '%.*s'", __FUNCTION__, len, len, data); - - cbuffer_write( input, data, len ); - - while ( input->count > 0 ) - { - /* skip over unwanted data, if any */ - while (h->overflow > 0) { - uint8_t* dummy; - int avail = cbuffer_read_peek( input, &dummy ); - - if (avail == 0) - return; - - if (avail > h->overflow) - avail = h->overflow; - - cbuffer_read_step( input, avail ); - h->overflow -= avail; - } + QemudClient* client; - /* all incoming messages are made of a 4-byte hexchar sequence giving */ - /* the length of the following payload */ - if (h->wanted == 0) - { - char header[4]; - int len; + client = qemud_client_new( service, channel, + opaque, + _hw_control_qemud_client_recv, + NULL ); - if (input->count < 4) - return; - - cbuffer_read( input, header, 4 ); - len = hex2int( (uint8_t*)header, 4 ); - if (len >= 0) { - /* if the message is too long, skip it */ - if (len > input->size) { - T("%s: skipping oversized message (%d > %d)", - __FUNCTION__, len, input->size); - h->overflow = len; - } else { - T("%s: waiting for %d bytes", __FUNCTION__, len); - h->wanted = len; - } - } - } - else - { - if (input->count < h->wanted) - break; - - cbuffer_read( input, h->query, h->wanted ); - h->query[h->wanted] = 0; - hw_control_do_query( h, (uint8_t*)h->query, h->wanted ); - h->wanted = 0; - } - } + qemud_client_set_framing(client, 1); + return client; } @@ -160,11 +93,11 @@ hw_control_do_query( HwControl* h, { uint8_t* q; - D("%s: query %4d '%.*s'", __FUNCTION__, querylen, querylen, query ); + T("%s: query %4d '%.*s'", __FUNCTION__, querylen, querylen, query ); q = if_starts_with( query, querylen, "power:light:brightness:" ); if (q != NULL) { - if (hw_control_funcs.light_brightness) { + if (h->client_funcs.light_brightness) { char* qq = strchr((const char*)q, ':'); int value; if (qq == NULL) { @@ -173,32 +106,30 @@ hw_control_do_query( HwControl* h, } *qq++ = 0; value = atoi(qq); - hw_control_funcs.light_brightness( hw_control_client, (char*)q, value ); + h->client_funcs.light_brightness( h->client, (char*)q, value ); } return; } } +static void +hw_control_init( HwControl* control, + void* client, + const AndroidHwControlFuncs* client_funcs ) +{ + control->client = client; + control->client_funcs = client_funcs[0]; + control->service = qemud_service_register( "hw-control", 0, + control, + _hw_control_qemud_connect ); +} + void android_hw_control_init( void* opaque, const AndroidHwControlFuncs* funcs ) { - static CharDriverState* hw_control_cs; - static HwControl hwstate[1]; - - if (hw_control_cs == NULL) { - CharDriverState* cs; - if ( android_qemud_get_channel( ANDROID_QEMUD_CONTROL, &cs ) < 0 ) { - derror( "could not create hardware control charpipe" ); - exit(1); - } - - hw_control_cs = cs; - hw_control_init( hwstate, cs ); - qemu_chr_add_handlers( cs, hw_control_can_read, hw_control_read, NULL, hwstate ); + static HwControl hwstate[1]; - D("%s: hw-control char pipe initialized", __FUNCTION__); - } - hw_control_client = opaque; - hw_control_funcs = funcs[0]; + hw_control_init(hwstate, opaque, funcs); + D("%s: hw-control qemud handler initialized", __FUNCTION__); } diff --git a/android/hw-qemud.c b/android/hw-qemud.c new file mode 100644 index 0000000..bebf221 --- /dev/null +++ b/android/hw-qemud.c @@ -0,0 +1,1194 @@ +/* Copyright (C) 2007-2008 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ +#include "android/hw-qemud.h" +#include "android/utils/debug.h" +#include "android/utils/misc.h" +#include "android/utils/system.h" +#include "android/utils/bufprint.h" +#include "qemu-char.h" +#include "charpipe.h" +#include "cbuffer.h" + +#define D(...) VERBOSE_PRINT(qemud,__VA_ARGS__) +#define D_ACTIVE VERBOSE_CHECK(qemud) + +/* the T(...) macro is used to dump traffic */ +#define T_ACTIVE 0 + +#if T_ACTIVE +#define T(...) VERBOSE_PRINT(qemud,__VA_ARGS__) +#else +#define T(...) ((void)0) +#endif + +/* max serial MTU. Don't change this without modifying + * development/emulator/qemud/qemud.c as well. + */ +#define MAX_SERIAL_PAYLOAD 4000 + +/* max framed data payload. Must be < (1 << 16) + */ +#define MAX_FRAME_PAYLOAD 65535 + + +/* + * 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:<hxid>:<name> + * + * where <name> is the service name, and <hxid> is a 4-hexchar + * number corresponding to the channel number. + * + * * in case of success, the emulator responds through channel 0 + * with: + * + * ok:connect:<hxid> + * + * 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:<hxid> + * + * * if the emulator refuses the service connection, it will + * send the following through channel 0: + * + * ko:connect:<hxid>: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). + */ + +/* + * IMPLEMENTATION DETAILS: + * + * We use one charpipe to connect the emulated serial port to the 'QemudSerial' + * object. This object is used to receive data from the serial port, and + * unframe messages (i.e. extract payload length + channel id from header, + * then the payload itself), before sending them to a generic receiver. + * + * The QemudSerial object can also be used to send messages to the daemon + * through the serial port (see qemud_serial_send()) + * + * The multiplexer is connected to one or more 'service' objects. + * are themselves connected through a charpipe to an emulated device or + * control sub-module in the emulator. + * + * tty <==charpipe==> QemudSerial ---> QemudMultiplexer ----> QemudClient + * ^ | + * | | + * +--------------------------------------+ + * + */ + +/** HANDLING INCOMING DATA FRAMES + **/ + +typedef struct QemudSink { + int len; + int size; + uint8_t* buff; +} QemudSink; + +static void +qemud_sink_reset( QemudSink* ss, int size, uint8_t* buffer ) +{ + ss->len = 0; + ss->size = size; + ss->buff = buffer; +} + +static int +qemud_sink_fill( QemudSink* ss, const uint8_t* *pmsg, int *plen) +{ + int avail = ss->size - ss->len; + + if (avail <= 0) + return 1; + + if (avail > *plen) + avail = *plen; + + memcpy(ss->buff + ss->len, *pmsg, avail); + *pmsg += avail; + *plen -= avail; + ss->len += avail; + + return (ss->len == ss->size); +} + +static int +qemud_sink_needed( QemudSink* ss ) +{ + return ss->size - ss->len; +} + +/** HANDLING SERIAL PORT CONNECTION + **/ + +/* The QemudSerial object receives data from the serial port charpipe. + * It parses the header to extract the channel id and payload length, + * then the message itself. + * + * Incoming messages are sent to a generic receiver identified by + * the 'recv_opaque' and 'recv_func' parameters to qemud_serial_init() + * + * It also provides qemud_serial_send() which can be used to send + * messages back through the serial port. + */ + +#define HEADER_SIZE 6 + +#define LENGTH_OFFSET 2 +#define LENGTH_SIZE 4 + +#define CHANNEL_OFFSET 0 +#define CHANNEL_SIZE 2 + +/* length of the framed header */ +#define FRAME_HEADER_SIZE 4 + +#define BUFFER_SIZE MAX_SERIAL_PAYLOAD + +/* out of convenience, the incoming message is zero-terminated + * and can be modified by the receiver (e.g. for tokenization). + */ +typedef void (*QemudSerialReceive)( void* opaque, int channel, uint8_t* msg, int msglen); + +typedef struct QemudSerial { + CharDriverState* cs; /* serial charpipe endpoint */ + + /* managing incoming packets from the serial port */ + ABool need_header; + int overflow; + int in_size; + int in_channel; + QemudSink header[1]; + QemudSink payload[1]; + uint8_t data0[MAX_SERIAL_PAYLOAD+1]; + + /* receiver */ + QemudSerialReceive recv_func; /* receiver callback */ + void* recv_opaque; /* receiver user-specific data */ +} QemudSerial; + + +/* called by the charpipe to see how much bytes can be + * read from the serial port. + */ +static int +qemud_serial_can_read( void* opaque ) +{ + QemudSerial* s = opaque; + + if (s->overflow > 0) { + return s->overflow; + } + + /* if in_size is 0, we're reading the header */ + if (s->need_header) + return qemud_sink_needed(s->header); + + /* otherwise, we're reading the payload */ + return qemud_sink_needed(s->payload); +} + +/* called by the charpipe to read data from the serial + * port. 'len' cannot be more than the value returned + * by 'qemud_serial_can_read'. + */ +static void +qemud_serial_read( void* opaque, const uint8_t* from, int len ) +{ + QemudSerial* s = opaque; + + T("%s: received %3d bytes: '%s'", __FUNCTION__, len, quote_bytes((const void*)from, len)); + + while (len > 0) { + int avail; + + /* skip overflow bytes */ + if (s->overflow > 0) { + avail = s->overflow; + if (avail > len) + avail = len; + + from += avail; + len -= avail; + continue; + } + + /* read header if needed */ + if (s->need_header) { + if (!qemud_sink_fill(s->header, (const uint8_t**)&from, &len)) + break; + + /* 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 ); + s->header->len = 0; + + if (s->in_size <= 0 || s->in_channel < 0) { + D("%s: bad header: '%.*s'", __FUNCTION__, HEADER_SIZE, s->data0); + continue; + } + + if (s->in_size > MAX_SERIAL_PAYLOAD) { + D("%s: ignoring huge serial packet: length=%d channel=%1", + __FUNCTION__, s->in_size, s->in_channel); + s->overflow = s->in_size; + continue; + } + + /* prepare 'in_data' for payload */ + s->need_header = 0; + qemud_sink_reset(s->payload, s->in_size, s->data0); + } + + /* read payload bytes */ + if (!qemud_sink_fill(s->payload, &from, &len)) + break; + + /* zero-terminate payload, then send it to receiver */ + s->payload->buff[s->payload->size] = 0; + D("%s: channel=%2d len=%3d '%s'", __FUNCTION__, + s->in_channel, s->payload->size, + quote_bytes((const void*)s->payload->buff, s->payload->size)); + + s->recv_func( s->recv_opaque, s->in_channel, s->payload->buff, s->payload->size ); + + /* prepare for new header */ + s->need_header = 1; + } +} + +/* intialize a QemudSerial object with a charpipe endpoint + * and a receiver. + */ +static void +qemud_serial_init( QemudSerial* s, + CharDriverState* cs, + QemudSerialReceive recv_func, + void* recv_opaque ) +{ + s->cs = cs; + s->recv_func = recv_func; + s->recv_opaque = recv_opaque; + s->need_header = 1; + s->overflow = 0; + + qemud_sink_reset( s->header, HEADER_SIZE, s->data0 ); + s->in_size = 0; + s->in_channel = -1; + + qemu_chr_add_handlers( cs, + qemud_serial_can_read, + qemud_serial_read, + NULL, + s ); +} + +/* send a message to the serial port. This will add the necessary + * header. + */ +static void +qemud_serial_send( QemudSerial* s, + int channel, + ABool framing, + const uint8_t* msg, + int msglen ) +{ + uint8_t header[HEADER_SIZE]; + uint8_t frame[FRAME_HEADER_SIZE]; + int avail, len = msglen; + + if (msglen <= 0 || channel < 0) + return; + + D("%s: channel=%2d len=%3d '%s'", + __FUNCTION__, channel, msglen, + quote_bytes((const void*)msg, msglen)); + + if (framing) { + len += FRAME_HEADER_SIZE; + } + + /* packetize the payload for the serial MTU */ + while (len > 0) + { + avail = len; + if (avail > MAX_SERIAL_PAYLOAD) + avail = MAX_SERIAL_PAYLOAD; + + /* write this packet's header */ + int2hex(header + LENGTH_OFFSET, LENGTH_SIZE, avail); + int2hex(header + CHANNEL_OFFSET, CHANNEL_SIZE, channel); + qemu_chr_write(s->cs, header, HEADER_SIZE); + + /* insert frame header when needed */ + if (framing) { + int2hex(frame, FRAME_HEADER_SIZE, msglen); + qemu_chr_write(s->cs, frame, FRAME_HEADER_SIZE); + avail -= FRAME_HEADER_SIZE; + len -= FRAME_HEADER_SIZE; + framing = 0; + } + + /* write message content */ + qemu_chr_write(s->cs, msg, avail); + msg += avail; + len -= avail; + } +} + +/** CLIENTS + **/ + +/* A QemudClient models a single client as seen by the emulator. + * Each client has its own channel id, and belongs to a given + * QemudService (see below). + * + * There is a global list of clients used to multiplex incoming + * messages from the channel id (see qemud_multiplexer_serial_recv()). + * + */ + +struct QemudClient { + int channel; + QemudSerial* serial; + void* clie_opaque; + QemudClientRecv clie_recv; + QemudClientClose clie_close; + QemudService* service; + QemudClient* next_serv; /* next in same service */ + QemudClient* next; + QemudClient** pref; + + /* framing support */ + int framing; + ABool need_header; + QemudSink header[1]; + uint8_t header0[FRAME_HEADER_SIZE]; + QemudSink payload[1]; +}; + +static void qemud_service_remove_client( QemudService* service, + QemudClient* client ); + +/* remove a QemudClient from global list */ +static void +qemud_client_remove( QemudClient* c ) +{ + c->pref[0] = c->next; + if (c->next) + c->next->pref = c->pref; + + c->next = NULL; + c->pref = &c->next; +} + +/* add a QemudClient to global list */ +static void +qemud_client_prepend( QemudClient* c, QemudClient** plist ) +{ + c->next = *plist; + c->pref = plist; + *plist = c; + if (c->next) + c->next->pref = &c->next; +} + +/* receive a new message from a client, and dispatch it to + * the real service implementation. + */ +static void +qemud_client_recv( void* opaque, uint8_t* msg, int msglen ) +{ + QemudClient* c = opaque; + + /* no framing, things are simple */ + if (!c->framing) { + if (c->clie_recv) + c->clie_recv( c->clie_opaque, msg, msglen ); + return; + } + + /* framing */ + +#if 1 + /* special case, in 99% of cases, everything is in + * the incoming message, and we can do all we need + * directly without dynamic allocation. + */ + if (msglen > FRAME_HEADER_SIZE && + c->need_header == 1 && + qemud_sink_needed(c->header) == 0) + { + int len = hex2int( msg, FRAME_HEADER_SIZE ); + + if (len >= 0 && msglen == len + FRAME_HEADER_SIZE) { + if (c->clie_recv) + c->clie_recv( c->clie_opaque, + msg+FRAME_HEADER_SIZE, + msglen-FRAME_HEADER_SIZE); + return; + } + } +#endif + + while (msglen > 0) { + /* read the header */ + if (c->need_header) { + int frame_size; + uint8_t* data; + + if (!qemud_sink_fill(c->header, (const uint8_t**)&msg, &msglen)) + break; + + frame_size = hex2int(c->header0, 4); + if (frame_size == 0) { + D("%s: ignoring empty frame", __FUNCTION__); + continue; + } + if (frame_size < 0) { + D("%s: ignoring corrupted frame header '.*s'", + __FUNCTION__, FRAME_HEADER_SIZE, c->header0 ); + continue; + } + + AARRAY_NEW(data, frame_size+1); /* +1 for terminating zero */ + qemud_sink_reset(c->payload, frame_size, data); + c->need_header = 0; + c->header->len = 0; + } + + /* read the payload */ + if (!qemud_sink_fill(c->payload, (const uint8_t**)&msg, &msglen)) + break; + + c->payload->buff[c->payload->size] = 0; + + + if (c->clie_recv) + c->clie_recv( c->clie_opaque, c->payload->buff, c->payload->size ); + + AFREE(c->payload->buff); + c->need_header = 1; + } +} + +/* disconnect a client. this automatically frees the QemudClient. + * note that this also removes the client from the global list + * and from its service's list, if any. + */ +static void +qemud_client_disconnect( void* opaque ) +{ + QemudClient* c = opaque; + + /* remove from current list */ + qemud_client_remove(c); + + /* send a disconnect command to the daemon */ + if (c->channel > 0) { + char tmp[128], *p=tmp, *end=p+sizeof(tmp); + p = bufprint(tmp, end, "disconnect:%04x", c->channel); + qemud_serial_send(c->serial, 0, 0, (uint8_t*)tmp, p-tmp); + } + + /* call the client close callback */ + if (c->clie_close) { + c->clie_close(c->clie_opaque); + c->clie_close = NULL; + } + c->clie_recv = NULL; + + /* remove from service list, if any */ + if (c->service) { + qemud_service_remove_client(c->service, c); + c->service = NULL; + } + + AFREE(c); +} + +/* allocate a new QemudClient object */ +static QemudClient* +qemud_client_alloc( int channel_id, + void* clie_opaque, + QemudClientRecv clie_recv, + QemudClientClose clie_close, + QemudSerial* serial, + QemudClient** pclients ) +{ + QemudClient* c; + + ANEW0(c); + + c->serial = serial; + c->channel = channel_id; + c->clie_opaque = clie_opaque; + c->clie_recv = clie_recv; + c->clie_close = clie_close; + + c->framing = 0; + c->need_header = 1; + qemud_sink_reset(c->header, FRAME_HEADER_SIZE, c->header0); + + qemud_client_prepend(c, pclients); + + return c; +} + +/** SERVICES + **/ + +/* A QemudService models a _named_ service facility implemented + * by the emulator, that clients in the emulated system can connect + * to. + * + * Each service can have a limit on the number of clients they + * accept (this number if unlimited if 'max_clients' is 0). + * + * Each service maintains a list of active QemudClients and + * can also be used to create new QemudClient objects through + * its 'serv_opaque' and 'serv_connect' fields. + */ +struct QemudService { + const char* name; + int max_clients; + int num_clients; + QemudClient* clients; + QemudServiceConnect serv_connect; + void* serv_opaque; + QemudService* next; +}; + +/* Create a new QemudService object */ +static QemudService* +qemud_service_new( const char* name, + int max_clients, + void* serv_opaque, + QemudServiceConnect serv_connect, + QemudService** pservices ) +{ + QemudService* s; + + ANEW0(s); + s->name = ASTRDUP(name); + s->max_clients = max_clients; + s->num_clients = 0; + s->clients = NULL; + + s->serv_opaque = serv_opaque; + s->serv_connect = serv_connect; + + s->next = *pservices; + *pservices = s; + + return s; +} + +/* used internally to populate a QemudService object with a + * new QemudClient */ +static void +qemud_service_add_client( QemudService* s, QemudClient* c ) +{ + c->service = s; + c->next_serv = s->clients; + s->clients = c; + s->num_clients += 1; +} + +/* used internally to remove a QemudClient from a QemudService */ +static void +qemud_service_remove_client( QemudService* s, QemudClient* c ) +{ + QemudClient** pnode = &s->clients; + QemudClient* node; + + /* remove from clients linked-list */ + for (;;) { + node = *pnode; + if (node == NULL) { + D("%s: could not find client %d for service '%s'", + __FUNCTION__, c->channel, s->name); + return; + } + if (node == c) + break; + pnode = &node->next_serv; + } + + *pnode = node->next_serv; + s->num_clients -= 1; +} + +/** MULTIPLEXER + **/ + +/* A QemudMultiplexer object maintains the global state of the + * qemud service facility. It holds a QemudSerial object to + * maintain the state of the serial port connection. + * + * The QemudMultiplexer receives all incoming messages from + * the serial port, and dispatches them to the appropriate + * QemudClient. + * + * It also has a global list of clients, and a global list of + * services. + * + * Finally, the QemudMultiplexer has a special QemudClient used + * to handle channel 0, i.e. the control channel used to handle + * connections and disconnections of clients. + */ +typedef struct QemudMultiplexer QemudMultiplexer; + +struct QemudMultiplexer { + QemudSerial serial[1]; + QemudClient* clients; + QemudService* services; +}; + +/* this is the serial_recv callback that is called + * whenever an incoming message arrives through the serial port + */ +static void +qemud_multiplexer_serial_recv( void* opaque, + int channel, + uint8_t* msg, + int msglen ) +{ + QemudMultiplexer* m = opaque; + QemudClient* c = m->clients; + + /* dispatch to an existing client if possible + * note that channel 0 is handled by a special + * QemudClient that is setup in qemud_multiplexer_init() + */ + for ( ; c != NULL; c = c->next ) { + if (c->channel == channel) { + qemud_client_recv(c, msg, msglen); + return; + } + } + + D("%s: ignoring %d bytes for unknown channel %d", + __FUNCTION__, msglen, channel); +} + +/* handle a new connection attempt. This returns 0 on + * success, -1 if the service name is unknown, or -2 + * if the service's maximum number of clients has been + * reached. + */ +static int +qemud_multiplexer_connect( QemudMultiplexer* m, + const char* service_name, + int channel_id ) +{ + QemudService* sv; + QemudClient* client; + + /* find the corresponding registered service by name */ + for (sv = m->services; sv != NULL; sv = sv->next) { + if (!strcmp(sv->name, service_name)) { + break; + } + } + + if (sv == NULL) { + D("%s: no registered '%s' service", __FUNCTION__, service_name); + return -1; + } + + /* check service's client count */ + if (sv->max_clients > 0 && sv->num_clients >= sv->max_clients) { + D("%s: registration failed for '%s' service: too many clients (%d)", + __FUNCTION__, service_name, sv->num_clients); + return -2; + } + + /* ask the service to create a new QemudClient. Note that we + * assume that this calls qemud_client_new() which will add + * the client to the service's list automatically. + */ + client = sv->serv_connect( sv->serv_opaque, sv, channel_id ); + if (client == NULL) { + D("%s: registration failed for '%s' service", + __FUNCTION__, service_name); + return -1; + } + + D("%s: registered client channel %d for '%s' service", + __FUNCTION__, channel_id, service_name); + return 0; +} + +/* disconnect a given client from its channel id */ +static void +qemud_multiplexer_disconnect( QemudMultiplexer* m, + int channel ) +{ + QemudClient* c; + + /* find the client by its channel id, then disconnect it */ + for (c = m->clients; c; c = c->next) { + if (c->channel == channel) { + D("%s: disconnecting client %d", + __FUNCTION__, channel); + /* note thatt this removes the client from + * m->clients automatically. + */ + c->channel = -1; /* no need to send disconnect:<id> */ + qemud_client_disconnect(c); + return; + } + } + D("%s: disconnecting unknown channel %d", + __FUNCTION__, channel); +} + +/* handle control messages. This is used as the receive + * callback for the special QemudClient setup to manage + * channel 0. + * + * note that the message is zero-terminated for convenience + * (i.e. msg[msglen] is a valid memory read that returns '\0') + */ +static void +qemud_multiplexer_control_recv( void* opaque, + uint8_t* msg, + int msglen ) +{ + QemudMultiplexer* mult = opaque; + uint8_t* msgend = msg + msglen; + char tmp[64], *p=tmp, *end=p+sizeof(tmp); + + /* handle connection attempts. + * the client message must be "connect:<service-name>:<id>" + * where <id> is a 4-char hexadecimal string, which must be > 0 + */ + if (msglen > 8 && !memcmp(msg, "connect:", 8)) + { + const char* service_name = (const char*)msg + 8; + int channel, ret; + char* q; + + q = strchr(service_name, ':'); + if (q == NULL || q+5 != (char*)msgend) { + D("%s: malformed 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, 4); + if (channel <= 0) { + D("%s: malformed channel id '%.*s", + __FUNCTION__, 4, q); + return; + } + + ret = qemud_multiplexer_connect(mult, service_name, channel); + /* the answer can be one of: + * ok:connect:<id> + * ko:connect:<id>:<reason-for-failure> + */ + if (ret < 0) { + if (ret == -1) { + /* could not connect */ + p = bufprint(tmp, end, "ko:connect:%04x:unknown service", channel); + } else { + p = bufprint(tmp, end, "ko:connect:%04x:service busy", channel); + } + } + else { + p = bufprint(tmp, end, "ok:connect:%04x", channel); + } + qemud_serial_send(mult->serial, 0, 0, (uint8_t*)tmp, p-tmp); + return; + } + + /* handle client disconnections, + * this message arrives when the client has closed the connection. + * format: "disconnect:<id>" where <id> is a 4-hex channel id > 0 + */ + if (msglen == 15 && !memcmp(msg, "disconnect:", 11)) { + int channel_id = hex2int(msg+11, 4); + if (channel_id <= 0) { + D("%s: malformed disconnect channel id: '%.*s'", + __FUNCTION__, 4, msg+11); + return; + } + qemud_multiplexer_disconnect(mult, channel_id); + return; + } + + /* anything else is a problem */ + p = bufprint(tmp, end, "ko:unknown command"); + qemud_serial_send(mult->serial, 0, 0, (uint8_t*)tmp, p-tmp); +} + +/* initialize the global QemudMultiplexer. + */ +static void +qemud_multiplexer_init( QemudMultiplexer* mult, + CharDriverState* serial_cs ) +{ + QemudClient* control; + + /* initialize serial handler */ + qemud_serial_init( mult->serial, + serial_cs, + qemud_multiplexer_serial_recv, + mult ); + + /* setup listener for channel 0 */ + control = qemud_client_alloc( 0, + mult, + qemud_multiplexer_control_recv, + NULL, + mult->serial, + &mult->clients ); +} + +/* the global multiplexer state */ +static QemudMultiplexer _multiplexer[1]; + +/** HIGH-LEVEL API + **/ + +/* this function must be used in the serv_connect callback + * of a given QemudService object (see qemud_service_register() + * below). It is used to register a new QemudClient to acknowledge + * a new client connection. + * + * 'clie_opaque', 'clie_recv' and 'clie_close' are used to + * send incoming client messages to the corresponding service + * implementation, or notify the service that a client has + * disconnected. + */ +QemudClient* +qemud_client_new( QemudService* service, + int channelId, + void* clie_opaque, + QemudClientRecv clie_recv, + QemudClientClose clie_close ) +{ + QemudMultiplexer* m = _multiplexer; + QemudClient* c = qemud_client_alloc( channelId, + clie_opaque, + clie_recv, + clie_close, + m->serial, + &m->clients ); + + qemud_service_add_client(service, c); + return c; +} + +/* this can be used by a service implementation to send an answer + * or message to a specific client. + */ +void +qemud_client_send ( QemudClient* client, const uint8_t* msg, int msglen ) +{ + qemud_serial_send(client->serial, client->channel, client->framing != 0, msg, msglen); +} + +/* enable framing for this client. When TRUE, this will + * use internally a simple 4-hexchar header before each + * message exchanged through the serial port. + */ +void +qemud_client_set_framing( QemudClient* client, int framing ) +{ + /* release dynamic buffer if we're disabling framing */ + if (client->framing) { + if (!client->need_header) { + AFREE(client->payload->buff); + client->need_header = 1; + } + } + client->framing = !!framing; +} + +/* this can be used by a service implementation to close a + * specific client connection. + */ +void +qemud_client_close( QemudClient* client ) +{ + qemud_client_disconnect(client); +} + + + +/* this is the end of the serial charpipe that must be passed + * to the emulated tty implementation. The other end of the + * charpipe must be passed to qemud_multiplexer_init(). + */ +static CharDriverState* android_qemud_cs; + +extern void +android_qemud_init( void ) +{ + CharDriverState* cs; + + if (android_qemud_cs != NULL) + return; + + if (qemu_chr_open_charpipe( &android_qemud_cs, &cs ) < 0) { + derror( "%s: can't create charpipe to serial port", + __FUNCTION__ ); + exit(1); + } + + qemud_multiplexer_init(_multiplexer, cs); +} + +/* return the serial charpipe endpoint that must be used + * by the emulated tty implementation. + */ +CharDriverState* android_qemud_get_cs( void ) +{ + if (android_qemud_cs == NULL) + android_qemud_init(); + + return android_qemud_cs; +} + +/* this function is used to register a new named qemud-based + * service. You must provide 'serv_opaque' and 'serv_connect' + * which will be called whenever a new client tries to connect + * to the services. + * + * 'serv_connect' shall return NULL if the connection is refused, + * or a handle to a new QemudClient otherwise. The latter can be + * created through qemud_client_new() defined above. + * + * 'max_clients' is the maximum number of clients accepted by + * the service concurrently. If this value is 0, then any number + * of clients can connect. + */ +QemudService* +qemud_service_register( const char* service_name, + int max_clients, + void* serv_opaque, + QemudServiceConnect serv_connect ) +{ + QemudMultiplexer* m = _multiplexer; + QemudService* sv; + + if (android_qemud_cs == NULL) + android_qemud_init(); + + sv = qemud_service_new(service_name, + max_clients, + serv_opaque, + serv_connect, + &m->services); + + return sv; +} + +/* broadcast a given message to all clients of a given QemudService + */ +extern void +qemud_service_broadcast( QemudService* sv, + const uint8_t* msg, + int msglen ) +{ + QemudClient* c; + + for (c = sv->clients; c; c = c->next_serv) + qemud_client_send(c, msg, msglen); +} + + + +/* + * The following code is used for backwards compatibility reasons. + * It allows you to implement a given qemud-based service through + * a charpipe. + * + * In other words, this implements a QemudService and corresponding + * QemudClient that connects a qemud client running in the emulated + * system, to a CharDriverState object implemented through a charpipe. + * + * QemudCharClient <===charpipe====> (char driver user) + * + * For example, this is used to implement the "gsm" service when the + * modem emulation is provided through an external serial device. + * + * A QemudCharService can have only one client by definition. + * There is no QemudCharClient object because we can store a single + * CharDriverState handle in the 'opaque' field for simplicity. + */ + +typedef struct { + QemudService* service; + CharDriverState* cs; +} QemudCharService; + +/* called whenever a new message arrives from a qemud client. + * this simply sends the message through the charpipe to the user. + */ +static void +_qemud_char_client_recv( void* opaque, uint8_t* msg, int msglen ) +{ + CharDriverState* cs = opaque; + qemu_chr_write(cs, msg, msglen); +} + +/* we don't expect clients of char. services to exit. Just + * print an error to signal an unexpected situation. We should + * be able to recover from these though, so don't panic. + */ +static void +_qemud_char_client_close( void* opaque ) +{ + derror("unexpected qemud char. channel close"); +} + + +/* called by the charpipe to know how much data can be read from + * the user. Since we send everything directly to the serial port + * we can return an arbitrary number. + */ +static int +_qemud_char_service_can_read( void* opaque ) +{ + return 8192; /* whatever */ +} + +/* called to read data from the charpipe and send it to the client. + * used qemud_service_broadcast() even if there is a single client + * because we don't need a QemudCharClient object this way. + */ +static void +_qemud_char_service_read( void* opaque, const uint8_t* from, int len ) +{ + QemudService* sv = opaque; + qemud_service_broadcast( sv, from, len ); +} + +/* called when a qemud client tries to connect to a char. service. + * we simply create a new client and open the charpipe to receive + * data from it. + */ +static QemudClient* +_qemud_char_service_connect( void* opaque, QemudService* sv, int channel ) +{ + CharDriverState* cs = opaque; + QemudClient* c = qemud_client_new( sv, channel, + cs, + _qemud_char_client_recv, + _qemud_char_client_close); + + /* now we can open the gates :-) */ + qemu_chr_add_handlers( cs, + _qemud_char_service_can_read, + _qemud_char_service_read, + NULL, + sv ); + + return c; +} + +/* returns a charpipe endpoint that can be used by an emulated + * device or external serial port to implement a char. service + */ +int +android_qemud_get_channel( const char* name, CharDriverState* *pcs ) +{ + CharDriverState* cs; + + if (qemu_chr_open_charpipe(&cs, pcs) < 0) { + derror("can't open charpipe for '%s' qemud service", name); + exit(2); + } + qemud_service_register(name, 1, cs, _qemud_char_service_connect); + return 0; +} + +/* set the character driver state for a given qemud communication channel. this + * is used to attach the channel to an external char driver device directly. + * returns 0 on success, -1 on error + */ +int +android_qemud_set_channel( const char* name, CharDriverState* peer_cs ) +{ + qemud_service_register(name, 1, peer_cs, _qemud_char_service_connect); + return 0; +} diff --git a/android/qemud.h b/android/hw-qemud.h index 4fa71d0..7101c2a 100644 --- a/android/qemud.h +++ b/android/hw-qemud.h @@ -66,7 +66,41 @@ extern int android_qemud_set_channel( const char* name, CharDriverState* peer #define ANDROID_QEMUD_GSM "gsm" #define ANDROID_QEMUD_GPS "gps" #define ANDROID_QEMUD_CONTROL "control" +#define ANDROID_QEMUD_SENSORS "sensors" -/* add new channel names here when you need them */ +/* A QemudService service is used to connect one or more clients to + * a given emulator facility. Only one client can be connected at any + * given time, but the connection can be closed periodically. + */ + +typedef struct QemudClient QemudClient; +typedef struct QemudService QemudService; + + +typedef void (*QemudClientClose)( void* opaque ); +typedef void (*QemudClientRecv) ( void* opaque, uint8_t* msg, int msglen ); + +extern QemudClient* qemud_client_new( QemudService* service, + int channel_id, + void* clie_opaque, + QemudClientRecv clie_recv, + QemudClientClose clie_close ); + +extern void qemud_client_set_framing( QemudClient* client, int enabled ); + +extern void qemud_client_send ( QemudClient* client, const uint8_t* msg, int msglen ); +extern void qemud_client_close( QemudClient* client ); + + +typedef QemudClient* (*QemudServiceConnect)( void* opaque, QemudService* service, int channel ); + +extern QemudService* qemud_service_register( const char* serviceName, + int max_clients, + void* serv_opaque, + QemudServiceConnect serv_connect ); + +extern void qemud_service_broadcast( QemudService* sv, + const uint8_t* msg, + int msglen ); #endif /* _android_qemud_h */ diff --git a/android/hw-sensors.c b/android/hw-sensors.c new file mode 100644 index 0000000..eb2cc78 --- /dev/null +++ b/android/hw-sensors.c @@ -0,0 +1,450 @@ +/* Copyright (C) 2009 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ + +#include "android/hw-sensors.h" +#include "android/utils/debug.h" +#include "android/utils/misc.h" +#include "android/hw-qemud.h" +#include "android/globals.h" +#include "qemu-char.h" +#include "qemu-timer.h" + +#define D(...) VERBOSE_PRINT(sensors,__VA_ARGS__) + +/* define T_ACTIVE to 1 to debug transport communications */ +#define T_ACTIVE 0 + +#if T_ACTIVE +#define T(...) VERBOSE_PRINT(sensors,__VA_ARGS__) +#else +#define T(...) ((void)0) +#endif + +/* this code supports emulated sensor hardware + * + * Note that currently, only the accelerometer is really emulated, and only + * for the purpose of allowing auto-rotating the screen in keyboard-less + * configurations. + * + * + */ + + +static const struct { + const char* name; + int id; +} _sSensors[MAX_SENSORS] = { +#define SENSOR_(x,y) { y, ANDROID_SENSOR_##x }, + SENSORS_LIST +#undef SENSOR_ +}; + + +static int +_sensorIdFromName( const char* name ) +{ + int nn; + for (nn = 0; nn < MAX_SENSORS; nn++) + if (!strcmp(_sSensors[nn].name,name)) + return _sSensors[nn].id; + return -1; +} + + +typedef struct { + float x, y, z; +} Acceleration; + + +typedef struct { + float x, y, z; +} MagneticField; + + +typedef struct { + float azimuth; + float pitch; + float roll; +} Orientation; + + +typedef struct { + float celsius; +} Temperature; + + +typedef struct { + char enabled; + union { + Acceleration acceleration; + MagneticField magnetic; + Orientation orientation; + Temperature temperature; + } u; +} Sensor; + +/* + * - when the qemu-specific sensors HAL module starts, it sends + * "list-sensors" + * + * - this code replies with a string containing an integer corresponding + * to a bitmap of available hardware sensors in the current AVD + * configuration (e.g. "1" a.k.a (1 << ANDROID_SENSOR_ACCELERATION)) + * + * - the HAL module sends "set:<sensor>:<flag>" to enable or disable + * the report of a given sensor state. <sensor> must be the name of + * a given sensor (e.g. "accelerometer"), and <flag> must be either + * "1" (to enable) or "0" (to disable). + * + * - Once at least one sensor is "enabled", this code should periodically + * send information about the corresponding enabled sensors. The default + * period is 200ms. + * + * - the HAL module sends "set-delay:<delay>", where <delay> is an integer + * corresponding to a time delay in milli-seconds. This corresponds to + * a new interval between sensor events sent by this code to the HAL + * module. + * + * - the HAL module can also send a "wake" command. This code should simply + * send the "wake" back to the module. This is used internally to wake a + * blocking read that happens in a different thread. This ping-pong makes + * the code in the HAL module very simple. + * + * - each timer tick, this code sends sensor reports in the following + * format (each line corresponds to a different line sent to the module): + * + * acceleration:<x>:<y>:<z> + * magnetic-field:<x>:<y>:<z> + * orientation:<azimuth>:<pitch>:<roll> + * temperature:<celsius> + * sync:<time_us> + * + * Where each line before the sync:<time_us> is optional and will only + * appear if the corresponding sensor has been enabled by the HAL module. + * + * Note that <time_us> is the VM time in micro-seconds when the report + * was "taken" by this code. This is adjusted by the HAL module to + * emulated system time (using the first sync: to compute an adjustment + * offset). + */ +#define HEADER_SIZE 4 +#define BUFFER_SIZE 512 + +typedef struct { + QemudService* service; + int32_t delay_ms; + uint32_t enabledMask; + QEMUTimer* timer; + Sensor sensors[MAX_SENSORS]; +} HwSensors; + +/* forward */ + +static void hw_sensors_receive( HwSensors* h, + uint8_t* query, + int querylen ); + +static void hw_sensors_timer_tick(void* opaque); + +/* Qemud service management */ + +static void +_hw_sensors_qemud_client_recv( void* opaque, uint8_t* msg, int msglen ) +{ + hw_sensors_receive(opaque, msg, msglen); +} + +static QemudClient* +_hw_sensors_service_connect( void* opaque, QemudService* service, int channel ) +{ + HwSensors* sensors = opaque; + QemudClient* client = qemud_client_new(service, channel, + sensors, + _hw_sensors_qemud_client_recv, + NULL); + qemud_client_set_framing(client, 1); + return client; +} + +/* change the value of the emulated acceleration vector */ +static void +hw_sensors_set_acceleration( HwSensors* h, float x, float y, float z ) +{ + Sensor* s = &h->sensors[ANDROID_SENSOR_ACCELERATION]; + s->u.acceleration.x = x; + s->u.acceleration.y = y; + s->u.acceleration.z = z; +} + +#if 0 /* not used yet */ +/* change the value of the emulated magnetic vector */ +static void +hw_sensors_set_magnetic_field( HwSensors* h, float x, float y, float z ) +{ + Sensor* s = &h->sensors[ANDROID_SENSOR_MAGNETIC_FIELD]; + s->u.magnetic.x = x; + s->u.magnetic.y = y; + s->u.magnetic.z = z; +} + +/* change the values of the emulated orientation */ +static void +hw_sensors_set_orientation( HwSensors* h, float azimuth, float pitch, float roll ) +{ + Sensor* s = &h->sensors[ANDROID_SENSOR_MAGNETIC_FIELD]; + s->u.orientation.azimuth = azimuth; + s->u.orientation.pitch = pitch; + s->u.orientation.roll = roll; +} + +/* change the emulated temperature */ +static void +hw_sensors_set_temperature( HwSensors* h, float celsius ) +{ + Sensor* s = &h->sensors[ANDROID_SENSOR_MAGNETIC_FIELD]; + s->u.temperature.celsius = celsius; +} +#endif + +/* change the coarse orientation (landscape/portrait) of the emulated device */ +static void +hw_sensors_set_coarse_orientation( HwSensors* h, AndroidCoarseOrientation orient ) +{ + /* The Android framework computes the orientation by looking at + * the accelerometer sensor (*not* the orientation sensor !) + * + * That's because the gravity is a constant 9.81 vector that + * can be determined quite easily. + * + * Also, for some reason, the framework code considers that the phone should + * be inclined by 30 degrees along the phone's X axis to be considered + * in its ideal "vertical" position + * + * If the phone is completely vertical, rotating it will not do anything ! + */ + const double g = 9.81; + const double cos_30 = 0.866025403784; + const double sin_30 = 0.5; + + switch (orient) { + case ANDROID_COARSE_PORTRAIT: + hw_sensors_set_acceleration( h, 0., g*cos_30, g*sin_30 ); + break; + + case ANDROID_COARSE_LANDSCAPE: + hw_sensors_set_acceleration( h, g*cos_30, 0., g*sin_30 ); + break; + default: + ; + } +} + + +/* initialize the sensors state */ +static void +hw_sensors_init( HwSensors* h ) +{ + h->service = qemud_service_register("sensors", 1, h, + _hw_sensors_service_connect ); + h->enabledMask = 0; + h->delay_ms = 1000; + h->timer = qemu_new_timer(vm_clock, hw_sensors_timer_tick, h); + + hw_sensors_set_coarse_orientation(h, ANDROID_COARSE_PORTRAIT); +} + +/* send a one-line message to the HAL module through a qemud channel */ +static void +hw_sensors_send( HwSensors* hw, const uint8_t* msg, int msglen ) +{ + D("%s: '%s'", __FUNCTION__, quote_bytes((const void*)msg, msglen)); + qemud_service_broadcast(hw->service, msg, msglen); +} + +/* this function is called periodically to send sensor reports + * to the HAL module, and re-arm the timer if necessary + */ +static void +hw_sensors_timer_tick( void* opaque ) +{ + HwSensors* h = opaque; + int64_t delay = h->delay_ms; + int64_t now_ns; + uint32_t mask = h->enabledMask; + Sensor* sensor; + char buffer[128]; + + sensor = &h->sensors[ANDROID_SENSOR_ACCELERATION]; + if (sensor->enabled) { + snprintf(buffer, sizeof buffer, "acceleration:%g:%g:%g", + sensor->u.acceleration.x, + sensor->u.acceleration.y, + sensor->u.acceleration.z); + hw_sensors_send(h, (uint8_t*)buffer, strlen(buffer)); + } + + sensor = &h->sensors[ANDROID_SENSOR_MAGNETIC_FIELD]; + if (sensor->enabled) { + snprintf(buffer, sizeof buffer, "magnetic-field:%g:%g:%g", + sensor->u.magnetic.x, + sensor->u.magnetic.y, + sensor->u.magnetic.z); + hw_sensors_send(h, (uint8_t*)buffer, strlen(buffer)); + } + + sensor = &h->sensors[ANDROID_SENSOR_ORIENTATION]; + if (sensor->enabled) { + snprintf(buffer, sizeof buffer, "orientation:%g:%g:%g", + sensor->u.orientation.azimuth, + sensor->u.orientation.pitch, + sensor->u.orientation.roll); + hw_sensors_send(h, (uint8_t*)buffer, strlen(buffer)); + } + + sensor = &h->sensors[ANDROID_SENSOR_TEMPERATURE]; + if (sensor->enabled) { + snprintf(buffer, sizeof buffer, "temperature:%g", + sensor->u.temperature.celsius); + hw_sensors_send(h, (uint8_t*)buffer, strlen(buffer)); + } + + now_ns = qemu_get_clock(vm_clock); + + snprintf(buffer, sizeof buffer, "sync:%lld", now_ns/1000); + hw_sensors_send(h, (uint8_t*)buffer, strlen(buffer)); + + /* rearm timer, use a minimum delay of 20 ms, just to + * be safe. + */ + if (mask == 0) + return; + + if (delay < 20) + delay = 20; + + delay *= 1000000LL; /* convert to nanoseconds */ + qemu_mod_timer(h->timer, now_ns + delay); +} + +/* handle incoming messages from the HAL module */ +static void +hw_sensors_receive( HwSensors* hw, uint8_t* msg, int msglen ) +{ + D("%s: '%.*s'", __FUNCTION__, msglen, msg); + + /* "list-sensors" is used to get an integer bit map of + * available emulated sensors. We compute the mask from the + * current hardware configuration. + */ + if (msglen == 12 && !memcmp(msg, "list-sensors", 12)) { + char buff[12]; + int mask = 0; + + if (android_hw->hw_accelerometer) + mask |= (1 << ANDROID_SENSOR_ACCELERATION); + + /* XXX: TODO: Add other tests when we add the corresponding + * properties to hardware-properties.ini et al. */ + + snprintf(buff, sizeof buff, "%d", mask); + hw_sensors_send(hw, (const uint8_t*)buff, strlen(buff)); + return; + } + + /* "wake" is a special message that must be sent back through + * the channel. It is used to exit a blocking read. + */ + if (msglen == 4 && !memcmp(msg, "wake", 4)) { + hw_sensors_send(hw, (const uint8_t*)"wake", 4); + return; + } + + /* "set-delay:<delay>" is used to set the delay in milliseconds + * between sensor events + */ + if (msglen > 10 && !memcmp(msg, "set-delay:", 10)) { + hw->delay_ms = atoi((const char*)msg+10); + if (hw->enabledMask != 0) + hw_sensors_timer_tick(hw); + + return; + } + + /* "set:<name>:<state>" is used to enable/disable a given + * sensor. <state> must be 0 or 1 + */ + if (msglen > 4 && !memcmp(msg, "set:", 4)) { + char* q; + int id, enabled, oldEnabledMask = hw->enabledMask; + msg += 4; + q = strchr((char*)msg, ':'); + if (q == NULL) { /* should not happen */ + D("%s: ignore bad 'set' command", __FUNCTION__); + return; + } + *q++ = 0; + + id = _sensorIdFromName((const char*)msg); + if (id < 0) { + D("%s: ignore unknown sensor name '%s'", __FUNCTION__, msg); + return; + } + + enabled = (q[0] == '1'); + + hw->sensors[id].enabled = (char) enabled; + if (enabled) + hw->enabledMask |= (1 << id); + else + hw->enabledMask &= ~(1 << id); + + D("%s: %s %s sensor", __FUNCTION__, + hw->sensors[id].enabled ? "enabling" : "disabling", msg); + + if (oldEnabledMask == 0 && enabled) { + /* we enabled our first sensor, start event reporting */ + D("%s: starting event reporting (mask=%04x)", __FUNCTION__, + hw->enabledMask); + } + else if (hw->enabledMask == 0 && !enabled) { + /* we disabled our last sensor, stop event reporting */ + D("%s: stopping event reporting", __FUNCTION__); + } + hw_sensors_timer_tick(hw); + return; + } + + D("%s: ignoring unknown query", __FUNCTION__); +} + + +static HwSensors _sensorsState[1]; + +void +android_hw_sensors_init( void ) +{ + HwSensors* hw = _sensorsState; + + if (hw->service == NULL) { + hw_sensors_init(hw); + D("%s: sensors qemud service initialized", __FUNCTION__); + } +} + +/* change the coarse orientation value */ +extern void +android_sensors_set_coarse_orientation( AndroidCoarseOrientation orient ) +{ + android_hw_sensors_init(); + hw_sensors_set_coarse_orientation(_sensorsState, orient); +} + diff --git a/android/hw-sensors.h b/android/hw-sensors.h new file mode 100644 index 0000000..37bf20e --- /dev/null +++ b/android/hw-sensors.h @@ -0,0 +1,48 @@ +/* Copyright (C) 2009 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ +#ifndef _android_sensors_h +#define _android_sensors_h + +#include "qemu-common.h" + +/* initialize sensor emulation */ +extern void android_hw_sensors_init( void ); + +/* NOTE: this list must be the same that the one defined in + * the sensors_qemu.c source of the libsensors.goldfish.so + * library. + */ +#define SENSORS_LIST \ + SENSOR_(ACCELERATION,"acceleration") \ + SENSOR_(MAGNETIC_FIELD,"magnetic-field") \ + SENSOR_(ORIENTATION,"orientation") \ + SENSOR_(TEMPERATURE,"temperature") \ + +typedef enum { +#define SENSOR_(x,y) ANDROID_SENSOR_##x, + SENSORS_LIST +#undef SENSOR_ + MAX_SENSORS /* do not remove */ +} AndroidSensor; + +extern void android_hw_sensor_enable( AndroidSensor sensor ); + +/* COARSE ORIENTATION VALUES */ +typedef enum { + ANDROID_COARSE_PORTRAIT, + ANDROID_COARSE_LANDSCAPE +} AndroidCoarseOrientation; + +/* change the coarse orientation value */ +extern void android_sensors_set_coarse_orientation( AndroidCoarseOrientation orient ); + +#endif /* _android_gps_h */ diff --git a/android/main.c b/android/main.c index 38f14f5..b90cf90 100644 --- a/android/main.c +++ b/android/main.c @@ -48,9 +48,10 @@ #include "android/skin/keyset.h" #include "android/gps.h" -#include "android/qemud.h" +#include "android/hw-qemud.h" #include "android/hw-kmsg.h" #include "android/hw-control.h" +#include "android/hw-sensors.h" #include "android/user-config.h" #include "android/utils/bufprint.h" #include "android/utils/dirscanner.h" @@ -1873,7 +1874,7 @@ int main(int argc, char **argv) break; if (!path_exists(out)) { - derror("Can't access ANDROID_PRODUCT_OUT as '%s\n" + derror("Can't access ANDROID_PRODUCT_OUT as '%s'\n" "You need to build the Android system before launching the emulator", out); exit(2); @@ -1998,21 +1999,9 @@ int main(int argc, char **argv) exit(1); } } - else { - if (!opts->skin && android_build_out) { - /* select default skin based on product type */ - const char* p = strrchr(android_build_out,'/'); - if (p) { - if (p[1] == 's') { - opts->skin = "HVGA"; /* used to be QVGA-L */ - } else if (p[1] == 'd') { - opts->skin = "HVGA"; - } - } - D("autoconfig: -skin %s", opts->skin); - } - android_avdParams->skinName = opts->skin; - } + android_avdParams->skinName = opts->skin; + android_avdParams->skinRootPath = opts->skindir; + /* setup the virtual device differently depending on whether * we are in the Android build system or not */ @@ -2778,6 +2767,9 @@ void android_emulation_setup( void ) } while (0); + /* initialize sensors, this must be done here due to timer issues */ + android_hw_sensors_init(); + /* cool, now try to run the "ddms ping" command, which will take care of pinging usage * if the user agreed for it. the emulator itself never sends anything to any outside * machine @@ -2844,15 +2836,15 @@ void android_emulation_setup( void ) * under VMWare. */ BEGIN_NOSIGALRM - pid = fork(); + pid = fork(); + if (pid == 0) { + int fd = open("/dev/null", O_WRONLY); + dup2(fd, 1); + dup2(fd, 2); + execl( tmp, _ANDROID_PING_PROGRAM, "ping", "emulator", VERSION_STRING, NULL ); + } END_NOSIGALRM - if (pid == 0) { - int fd = open("/dev/null", O_WRONLY); - dup2(fd, 1); - dup2(fd, 2); - execl( tmp, _ANDROID_PING_PROGRAM, "ping", "emulator", VERSION_STRING, NULL ); - } /* don't do anything in the parent or in case of error */ strncat( tmp, " ping emulator " VERSION_STRING, PATH_MAX - strlen(tmp) ); D( "ping command: %s", tmp ); diff --git a/android/qemud.c b/android/qemud.c deleted file mode 100644 index b127fc9..0000000 --- a/android/qemud.c +++ /dev/null @@ -1,456 +0,0 @@ -/* Copyright (C) 2007-2008 The Android Open Source Project -** -** This software is licensed under the terms of the GNU General Public -** License version 2, as published by the Free Software Foundation, and -** may be copied, distributed, and modified under those terms. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -*/ -#include "android/qemud.h" -#include "android/utils/debug.h" -#include "android/utils/misc.h" -#include "qemu-char.h" -#include "charpipe.h" -#include "cbuffer.h" - -#define D(...) VERBOSE_PRINT(qemud,__VA_ARGS__) -#define D_ACTIVE VERBOSE_CHECK(qemud) - -/* the T(...) macro is used to dump traffic */ -#define T_ACTIVE 0 - -#if T_ACTIVE -#define T(...) VERBOSE_PRINT(qemud,__VA_ARGS__) -#else -#define T(...) ((void)0) -#endif - -#define MAX_PAYLOAD 4000 -#define MAX_CHANNELS 8 - -#define CHANNEL_CONTROL_INDEX 0 - -/** packets - **/ -#define HEADER_SIZE 6 - -typedef struct Packet { - struct Packet* next; - int len; - uint8_t header[HEADER_SIZE]; - uint8_t data[MAX_PAYLOAD]; -} Packet; - -static Packet* _free_packets; - -static void -packet_free( Packet* p ) -{ - p->next = _free_packets; - _free_packets = p; -} - -static Packet* -packet_alloc( void ) -{ - Packet* p = _free_packets; - if (p != NULL) { - _free_packets = p->next; - } else { - p = malloc(sizeof(*p)); - if (p == NULL) { - derror("%s: not enough memory", __FUNCTION__); - exit(1); - } - } - p->next = NULL; - p->len = 0; - return p; -} - -/** channels - **/ -typedef void (*EnqueueFunc)( void* user, Packet* p ); - -typedef struct { - const char* name; - int index; - CharDriverState* cs; - EnqueueFunc enq_func; - void* enq_user; -} Channel; - - -static int -channel_can_read( void* opaque ) -{ - Channel* c = opaque; - - return c->index < 0 ? 0 : MAX_PAYLOAD; -} - - -/* here, the data comes from the emulated device (e.g. GSM modem) through - * a charpipe, we simply need to send it through the multiplexer */ -static void -channel_read( void* opaque, const uint8_t* from, int len ) -{ - Channel* c = opaque; - - if (c->enq_func != NULL) { - Packet* p = packet_alloc(); - - if (len > MAX_PAYLOAD) - len = MAX_PAYLOAD; - - memcpy( p->data, from, len ); - p->len = len + HEADER_SIZE; - int2hex( p->header+0, 4, len ); - int2hex( p->header+4, 2, c->index ); - - c->enq_func( c->enq_user, p ); - } - else - { - D("%s: discarding %d bytes for channel '%s'", - __FUNCTION__, len, c->name); - } -} - -static void -channel_init( Channel* c, const char* name, CharDriverState* peer_cs ) -{ - c->name = name; - c->index = -1; - c->enq_func = NULL; - c->enq_user = NULL; - c->cs = peer_cs; -} - - -static void -channel_set_peer( Channel* c, int index, EnqueueFunc enq_func, void* enq_user ) -{ - c->index = index; - qemu_chr_add_handlers( c->cs, - channel_can_read, - channel_read, - NULL, - c ); - c->enq_func = enq_func; - c->enq_user = enq_user; -} - - -static int -channel_write( Channel*c , const uint8_t* buf, int len ) -{ - return qemu_chr_write( c->cs, buf, len ); -} - -/** multiplexer - **/ -#define IN_BUFF_SIZE (2*MAX_PAYLOAD) - -typedef struct { - CharDriverState* cs; - - CBuffer in_cbuffer[1]; - int in_datalen; - int in_channel; - - int count; - Channel channels[MAX_CHANNELS]; - uint8_t in_buff[ IN_BUFF_SIZE + HEADER_SIZE ]; -} Multiplexer; - - -/* called by channel_read when data comes from an emulated - * device, and needs to be multiplexed through the serial - * port - */ -static void -multiplexer_enqueue( Multiplexer* m, Packet* p ) -{ - T("%s: sending %d bytes: '%s'", __FUNCTION__, - p->len - HEADER_SIZE, quote_bytes( p->data, p->len - HEADER_SIZE ) ); - - qemu_chr_write( m->cs, p->header, HEADER_SIZE ); - qemu_chr_write( m->cs, p->data, p->len - HEADER_SIZE ); - packet_free(p); -} - -/* called when we received a channel registration from the - * qemud daemon - */ -static void -multiplexer_register_channel( Multiplexer* m, - const char* name, - int index ) -{ - Channel* c = m->channels; - Channel* c_end = c + m->count; - - for ( ; c < c_end; c++ ) { - if ( !strcmp(c->name, name) ) - break; - } - - if (c >= c_end) { - D( "%s: unknown channel name '%s'", - __FUNCTION__, name ); - return; - } - - if (c->index >= 0) { - D( "%s: channel '%s' re-assigned index %d", - __FUNCTION__, name, index ); - c->index = index; - return; - } - channel_set_peer( c, index, (EnqueueFunc) multiplexer_enqueue, m ); - D( "%s: channel '%s' registered as index %d", - __FUNCTION__, c->name, c->index ); -} - - -/* handle answers from the control channel */ -static void -multiplexer_handle_control( Multiplexer* m, Packet* p ) -{ - int len = p->len - HEADER_SIZE; - - /* for now, the only supported answer is 'ok:connect:<name>:<XX>' where - * <XX> is a hexdecimal channel numner */ - D( "%s: received '%s'", __FUNCTION__, quote_bytes( (const void*)p->data, (unsigned)len ) ); - if ( !memcmp( p->data, "ok:connect:", 11 ) ) do { - char* name = (char*)p->data + 11; - char* q = strchr( name, ':' ); - int index; - - if (q == NULL) - break; - - q[0] = 0; - if (q + 3 > (char*)p->data + len) - break; - - index = hex2int( (uint8_t*)q+1, 2 ); - if (index < 0) - break; - - multiplexer_register_channel( m, name, index ); - goto Exit; - } - while(0); - - D( "%s: unsupported message !!", __FUNCTION__ ); -Exit: - packet_free(p); -} - - -static int -multiplexer_can_read( void* opaque ) -{ - Multiplexer* m = opaque; - - return cbuffer_write_avail( m->in_cbuffer ); -} - -/* the data comes from the serial port, we need to reconstruct packets then - * dispatch them to the appropriate channel */ -static void -multiplexer_read( void* opaque, const uint8_t* from, int len ) -{ - Multiplexer* m = opaque; - CBuffer* cb = m->in_cbuffer; - int ret = 0; - - T("%s: received %d bytes from serial: '%s'", - __FUNCTION__, len, quote_bytes( from, len )); - - ret = cbuffer_write( cb, from, len ); - if (ret == 0) - return; - - for (;;) { - int len = cbuffer_read_avail( cb ); - - if (m->in_datalen == 0) { - uint8_t header[HEADER_SIZE]; - - if (len < HEADER_SIZE) - break; - - cbuffer_read( cb, header, HEADER_SIZE ); - m->in_datalen = hex2int( header+0, 4 ); - m->in_channel = hex2int( header+4, 2 ); - } - else - { - Packet* p; - - if (len < m->in_datalen) - break; - - /* a full packet was received */ - p = packet_alloc(); - cbuffer_read( cb, p->data, m->in_datalen ); - p->len = HEADER_SIZE + m->in_datalen; - - /* find the channel for this packet */ - if (m->in_channel == CHANNEL_CONTROL_INDEX) - multiplexer_handle_control( m, p ); - else { - Channel* c = m->channels; - Channel* c_end = c + m->count; - - for ( ; c < c_end; c++ ) { - if (c->index == m->in_channel) { - channel_write( c, p->data, m->in_datalen ); - break; - } - } - packet_free(p); - } - m->in_datalen = 0; - } - - } - return; -} - -static void -multiplexer_query_channel( Multiplexer* m, const char* name ) -{ - Packet* p = packet_alloc(); - int len; - - len = snprintf( (char*)p->data, MAX_PAYLOAD, "connect:%s", name ); - - int2hex( p->header+0, 4, len ); - int2hex( p->header+4, 2, CHANNEL_CONTROL_INDEX ); - p->len = HEADER_SIZE + len; - - multiplexer_enqueue( m, p ); -} - - -static Channel* -multiplexer_find_channel( Multiplexer* m, const char* name ) -{ - int n; - for (n = 0; n < m->count; n++) - if ( !strcmp(m->channels[n].name, name) ) - return m->channels + n; - - return NULL; -} - - -static Multiplexer _multiplexer[1]; -static CharDriverState* android_qemud_cs; - -extern void -android_qemud_init( void ) -{ - Multiplexer* m = _multiplexer; - - if (android_qemud_cs != NULL) - return; - - m->count = 0; - - cbuffer_reset( m->in_cbuffer, m->in_buff, sizeof(m->in_buff) ); - m->in_datalen = 0; - m->in_channel = 0; - - if (qemu_chr_open_charpipe( &android_qemud_cs, &m->cs ) < 0) { - derror( "%s: can't create charpipe to serial port", - __FUNCTION__ ); - exit(1); - } - - qemu_chr_add_handlers( m->cs, multiplexer_can_read, - multiplexer_read, NULL, m ); -} - - -CharDriverState* android_qemud_get_cs( void ) -{ - if (android_qemud_cs == NULL) - android_qemud_init(); - - return android_qemud_cs; -} - - -extern int -android_qemud_get_channel( const char* name, CharDriverState** pcs ) -{ - Multiplexer* m = _multiplexer; - Channel* c; - CharDriverState* peer_cs; - int ret; - - if (m->cs == NULL) - android_qemud_init(); - - c = multiplexer_find_channel( m, name ); - if (c) { - derror( "%s: trying to get already-opened qemud channel '%s'", - __FUNCTION__, name ); - return -1; - } - - if (m->count >= MAX_CHANNELS) { - derror( "%s: too many registered channels (%d)", - __FUNCTION__, m->count ); - return -1; - } - - c = m->channels + m->count; - - ret = qemu_chr_open_charpipe( &peer_cs, pcs ); - if (ret == 0) { - channel_init(c, name, peer_cs); - m->count += 1; - multiplexer_query_channel( m, c->name ); - } - - return ret; -} - -extern int -android_qemud_set_channel( const char* name, CharDriverState* peer_cs ) -{ - Multiplexer* m = _multiplexer; - Channel* c; - - if (m->cs == NULL) - android_qemud_init(); - - c = multiplexer_find_channel(m, name); - if (c != NULL) { - derror( "%s: trying to set opened qemud channel '%s'", - __FUNCTION__, name ); - return -1; - } - - if (m->count >= MAX_CHANNELS) { - derror( "%s: too many registered channels (%d)", - __FUNCTION__, m->count ); - return -1; - } - - c = m->channels + m->count; - channel_init(c, name, peer_cs); - m->count += 1; - multiplexer_query_channel( m, c->name ); - - return 0; -} diff --git a/android/skin/window.c b/android/skin/window.c index 13bdca9..7ce5759 100644 --- a/android/skin/window.c +++ b/android/skin/window.c @@ -15,6 +15,7 @@ #include "android/charmap.h" #include "android/utils/debug.h" #include "android/utils/display.h" +#include "android/hw-sensors.h" #include <SDL_syswm.h> #include "qemu-common.h" #include <math.h> @@ -1204,6 +1205,11 @@ skin_window_reset_internal ( SkinWindow* window, SkinLayout* slayout ) if (slayout->event_type != 0) { kbd_generic_event( slayout->event_type, slayout->event_code, slayout->event_value ); + /* XXX: hack, replace by better code here */ + if (slayout->event_value != 0) + android_sensors_set_coarse_orientation( ANDROID_COARSE_PORTRAIT ); + else + android_sensors_set_coarse_orientation( ANDROID_COARSE_LANDSCAPE ); } return 0; diff --git a/android/utils/debug.h b/android/utils/debug.h index fdf93c9..d6bd3f9 100644 --- a/android/utils/debug.h +++ b/android/utils/debug.h @@ -33,6 +33,7 @@ _VERBOSE_TAG(nand_limits, "nand/flash read/write thresholding") \ _VERBOSE_TAG(hw_control, "emulated power/flashlight/led/vibrator") \ _VERBOSE_TAG(avd_config, "android virtual device configuration") \ + _VERBOSE_TAG(sensors, "emulated sensors") \ #define _VERBOSE_TAG(x,y) VERBOSE_##x, typedef enum { |