Friday, March 21, 2014

Android, QEMU and the Camera - Emulating the Camera Hardware in Android (Part III)

This third post in the series about the Android QEMU camera discusses the camera service (as noted in part two, this should not be confused with the Android framework's Camera Service, which I refer to in capital letters).
The camera service code can be found in directory /external/qemu/android/camera.  The camera service is initialized from main() in /external/qemu/vl-android.c.  The main() function is interesting if you want to understand the emulator boot process, but be warned that this function is composed of 2000 lines of code!  In any case, main() invokes android_camera_service_init which is part of the narrow public interface of the camera service.  The initialization control flow is summarized below:

android_camera_service_init
    ==> _camera_service_init
        ==>  enumerate_camera_devices
    ==> qemud_service_register(SERVICE_NAME == "camera", _camera_service_connect)

Function _camera_service_init uses a structure of type AndroidHwConfig (defined in /external/qemu/android/avd/hw-config.h).  This structure contains "the hardware configuration for this specific virtual device", and more specifically it contains a description of the camera type (webcam, emulated, none) connected to the back and the front of the device.  This structure is basically a reflection of the AVD configuration file ($HOME/.android/avd/.avd/hardware-qemu.ini, on Linux) or the AVD description parameters passed to the emulator through the command line parameters. 
Function enumerate_camera_devices performs a basic discovery and interrogation of the camera devices on the host machine.  There is an implementation for Linux host machines (camera-capture-linux.c), for Windows host machines (camera-capture-windows.c), and for MAC host machines (camera-capture-mac.m).  In fact, all of the low-level camera access code is segregated in these threee files.  The Linux code uses the V4L2 API, of course and its enumerate_camera_devices  implementation opens a video device and enumerates available frame pixel formats, (skipping compressed formats) looking for a match to the requested formats.
Finally, function qemud_service_register registers the camera service with the hw_qemud (see previous post) under the service name "camera" and passes a callback which hw_qemud should invoke when camera service clients attempt to connect to the service.
Examining function _camera_service_connect reveals that the camera service supports two types of clients:

  • An emulated camera factory client; and
  • An emulated camera client

And this brings us almost full circle: class EmulatedCameraFactory  (discussed in the previous post) uses an emulated camera factory client (of class FactoryQemuClient) and class EmulatedQemuCameraDevice uses an emulated camera client (of class CameraQemuClient).

And now we can tie some loose ends from the previous post and take a deeper look at the control flow of loading the emulated Camera HAL module and creating an emulated camera device.  This is a good opportunity to remind ourselves that these posts only examined the emulated (web) cameras, not the emulated "fake" cameras and so there are a couple of shortcuts that I took in the flows below to prevent further confusion.

Invoked when camera.goldfish.so is loaded to memory and gEmulatedCameraFactory is instantiated:
   
EmulatedCameraFactory::EmulatedCameraFactory
    ==> FactoryQemuClient::connectClient
        ==> qemu_pipe_open("qemud:camera")
    ==> EmulatedCameraFactory::createQemuCameras
        ==> FactoryQemuClient::listCameras
            for each camera:
                ==> create EmulatedQemuCamera
                ==> EmulatedQemuCamera::Initialize
                    ==> EmulatedQemuCameraDevice::Initialize
                        ==> CameraQemuClient::connectClient
                            ==> qemu_pipe_open("qemud:camera:??")
                        ==> EmulatedCameraDevice::Initialize()

Invoked when the emulated HAL module is asked to open a camera HAL device:

hw_module_methods_t.open = EmulatedCameraFactory::device_open
    ==> EmulatedCameraFactory::cameraDeviceOpen
        ==> EmulatedCamera::connectCamera
            ==> EmulatedQemuCameraDevice::connectDevice
                ==> CameraQemuClient::queryConnect
                    ==> QemuClient::doQuery
                        ==> QemuClient::sendMessage
                            ==> qemud_fd_write

Once the camera devices are open and the communication path between the HAL camera device and the emulated web camera is open, communication continues to be facilitated via the CameraQemuClient using the query mechanism that we saw in the call flow above.  The query itself is a string composed of a query-identification-string (to identify what we are asking for: connect, disconnect, start, stop, frame) and a list of name-value strings (which depend on the query type).  This string is then written to the /dev/qemu device, and from there it makes its way through the goldfish_pipe, then to the hw_qemud service, and finally to the camera service.  There the query is parsed, acted upon (e.g. on a Linux host V4L2 commands are sent to the Host kernel to drive the USB webcam connected to the host), and a reply is sent.  The sender unblocks from the /dev/qemu read operation and completes its work.
     
Reference code:

  • device/generic/goldfish/ - Goldfish device
  • device/generic/goldfish/Camera/ - Goldfish camera
  • hardware/libhardware/include/hardware/qemu_pipe.h
  • linux/kernel/drivers/platform/goldfish/goldfish_pipe.c
  • external/qemu/hw/goldfish_pipe.c
  • external/qemu/android/hw-qemud.c
  • external/qemu/android/camera/
  • external/qemu/android/camera/camera-service.c
  • external/qemu/docs/ANDROID-QEMU-PIPE.TXT
  • hardware/libhardware/hardware.c