sensors data sent to / requested from android HAL

模拟器在硬件上为android提供虚拟sensors,通过模拟器console,或者模拟器sensors socket通道,我们可以从外界(比如eclipse调试工具)设置这些虚拟sensors的值

模拟器中运行的android,通过qemu sensor HAL拿到这些sensors值,qemu sensor HAL与实体手机的sensor HAL的区别在于:实体手机的sensor HAL通过底层Linux驱动获取真实sensors数据,qemu sensor HAL通过qemud的socket通道获取前面设置的虚拟sensors值

关于qemud,代码(qemud.c)中有一段介绍,很好说明了qemud与模拟器、qemu sensor HAL之间的关系:

  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
qemud是一个桥梁,emulator将从外部应用接受到的sensors数据,通过serial串口给qemud,qemud再通过socket通道将数据传递给连接进来的client,qemu sensor HAL就是client之一 init.goldfish.rc脚本的选择依据:查看/proc/cpuinfo文件,其中的Hardware字段表明了使用哪一个init.?.rc配置,模拟器为goldfish 可以看看这幅图,更好理解qemud与模拟器、qemu sensor HAL之间的关系 sensor data xfer 模拟器通过_hwSensorClient_receive()接受到Android HAL层传递过来的请求,通过qemud_client_send()将sensors数据发送给qemud,qemu sensor HAL通过qemud_channel_recv()从qemud拿到sensors数据

sensors数据格式

HAL层发送过来的请求数据主要包括这么几种格式: "list-sensors":列出支持的sensors列表,模拟器将返回一个int表示支持的sensors mask值 "wake":原样返回给HAL,让HAL退出poll循环 "set-delay:%d":设置sensors轮询间隔 "set:%s:%d":设置对应的sensor是否enable,例如"set:acceleration:1" 模拟器将虚拟sensors数据返回给HAL,格式如下: "acceleration:%g:%g:%g":获取加速度传感器数据 "orientation:%g:%g:%g":获取方向传感器数据 "magnetic:%g:%g:%g":获取磁力传感器数据 "temperature:%g":获取温度传感器数据 "proximity:%g":获取距离传感器数据 "sync:%lld":sent after a series of sensor events where 'time' is expressed in micro-seconds
下面来看看具体的代码 qemu sensor HAL层对应的代码位于device/generic/goldfish/sensors/sensors_qemu.c 模拟器的虚拟sensors代码位于android-4.4/external/qemu/android/hw-sensors.c
/* handle incoming messages from the HAL module */
static void
_hwSensorClient_receive( HwSensorClient*  cl, uint8_t*  msg, int  msglen )
{
    HwSensors*  hw = cl->sensors;

    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;
        int   nn;

        for (nn = 0; nn < MAX_SENSORS; nn++) {
            if (hw->sensors[nn].enabled)
                mask |= (1 << nn);
        }

        snprintf(buff, sizeof buff, "%d", mask);
        _hwSensorClient_send(cl, (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)) {
        _hwSensorClient_send(cl, (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)) {
        cl->delay_ms = atoi((const char*)msg+10);
        if (cl->enabledMask != 0)
            _hwSensorClient_tick(cl);

        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 = cl->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 || id >= MAX_SENSORS) {
            D("%s: ignore unknown sensor name '%s'", __FUNCTION__, msg);
            return;
        }

        if (!hw->sensors[id].enabled) {
            D("%s: trying to set disabled %s sensor", __FUNCTION__, msg);
            return;
        }
        enabled = (q[0] == '1');

        if (enabled)
            cl->enabledMask |= (1 << id);
        else
            cl->enabledMask &= ~(1 << id);

        if (cl->enabledMask != oldEnabledMask) {
            D("%s: %s %s sensor", __FUNCTION__,
                (cl->enabledMask & (1 << id))  ? "enabling" : "disabling",  msg);
        }

        /* If emulating device is connected update sensor state there too. */
        if (hw->sensors_port != NULL) {
            if (enabled) {
                sensors_port_enable_sensor(hw->sensors_port, (const char*)msg);
            } else {
                sensors_port_disable_sensor(hw->sensors_port, (const char*)msg);
            }
        }

        _hwSensorClient_tick(cl);
        return;
    }

    D("%s: ignoring unknown query", __FUNCTION__);
}
/* this function is called periodically to send sensor reports
 * to the HAL module, and re-arm the timer if necessary
 */
static void
_hwSensorClient_tick( void*  opaque )
{
    HwSensorClient*  cl = opaque;
    HwSensors*       hw  = cl->sensors;
    int64_t          delay = cl->delay_ms;
    int64_t          now_ns;
    uint32_t         mask  = cl->enabledMask;
    Sensor*          sensor;
    char             buffer[128];

    if (_hwSensorClient_enabled(cl, ANDROID_SENSOR_ACCELERATION)) {
        sensor = &hw->sensors[ANDROID_SENSOR_ACCELERATION];
        snprintf(buffer, sizeof buffer, "acceleration:%g:%g:%g",
                 sensor->u.acceleration.x,
                 sensor->u.acceleration.y,
                 sensor->u.acceleration.z);
        _hwSensorClient_send(cl, (uint8_t*)buffer, strlen(buffer));
    }

    if (_hwSensorClient_enabled(cl, ANDROID_SENSOR_MAGNETIC_FIELD)) {
        sensor = &hw->sensors[ANDROID_SENSOR_MAGNETIC_FIELD];
        /* NOTE: sensors HAL expects "magnetic", not "magnetic-field" name here. */
        snprintf(buffer, sizeof buffer, "magnetic:%g:%g:%g",
                 sensor->u.magnetic.x,
                 sensor->u.magnetic.y,
                 sensor->u.magnetic.z);
        _hwSensorClient_send(cl, (uint8_t*)buffer, strlen(buffer));
    }

    if (_hwSensorClient_enabled(cl, ANDROID_SENSOR_ORIENTATION)) {
        sensor = &hw->sensors[ANDROID_SENSOR_ORIENTATION];
        snprintf(buffer, sizeof buffer, "orientation:%g:%g:%g",
                 sensor->u.orientation.azimuth,
                 sensor->u.orientation.pitch,
                 sensor->u.orientation.roll);
        _hwSensorClient_send(cl, (uint8_t*)buffer, strlen(buffer));
    }

    if (_hwSensorClient_enabled(cl, ANDROID_SENSOR_TEMPERATURE)) {
        sensor = &hw->sensors[ANDROID_SENSOR_TEMPERATURE];
        snprintf(buffer, sizeof buffer, "temperature:%g",
                 sensor->u.temperature.celsius);
        _hwSensorClient_send(cl, (uint8_t*)buffer, strlen(buffer));
    }

    if (_hwSensorClient_enabled(cl, ANDROID_SENSOR_PROXIMITY)) {
        sensor = &hw->sensors[ANDROID_SENSOR_PROXIMITY];
        snprintf(buffer, sizeof buffer, "proximity:%g",
                 sensor->u.proximity.value);
        _hwSensorClient_send(cl, (uint8_t*) buffer, strlen(buffer));
    }

    now_ns = qemu_get_clock_ns(vm_clock);

    snprintf(buffer, sizeof buffer, "sync:%" PRId64, now_ns/1000);
    _hwSensorClient_send(cl, (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(cl->timer, now_ns + delay);
}

/* send a one-line message to the HAL module through a qemud channel */
static void
_hwSensorClient_send( HwSensorClient*  cl, const uint8_t*  msg, int  msglen )
{
    D("%s: '%s'", __FUNCTION__, quote_bytes((const void*)msg, msglen));
    qemud_client_send(cl->client, msg, msglen);
}