Cuttlefish control panel

The default WebRTC browser interface for Cuttlefish includes a control panel that enables more ways to interact with the virtual device.

The control panel features default buttons to simulate common physical device actions such as power button or volume buttons, as well as device rotation.

Custom actions

You can customize the control panel to add more buttons that allow your virtual device to more closely emulate your physical device. This is useful for testing features unique to your device, such as a hardware button or special gesture that triggers a unique action in the OS. You can also use custom buttons to enable testing more QA-focused features such as the behavior of your OS when the device is low battery.

The default Cuttlefish control panel includes support to "plug in" custom actions without needing to modify the main Cuttlefish AOSP project. Your virtual device needs to include only a minimal configuration file to start using custom actions. See this example custom action config file.

  1. Create a JSON file that defines your device's custom actions. You can put this file in any directory you own. The structure of this file is described in the ADB shell and Action server sections.

  2. Create a prebuilt_etc_host module for your JSON config. Ensure that the sub_dir is equal to cvd_custom_action_config.

    prebuilt_etc_host {
        // Use any name you choose.
        name: "my_custom_action_config.json",
        src: "my_custom_action_config.json",
        // Always use this sub_dir.
        sub_dir: "cvd_custom_action_config",
  3. Set Soong config build variables in your device's product makefile to configure the virtual device host package to include your custom action config file.

    # Set these variables exactly as shown here to enable the host package to see
    # your custom config module name.
    SOONG_CONFIG_cvd += custom_action_config
    # Set this value to the name of your JSON module.
    SOONG_CONFIG_cvd_custom_action_config := my_custom_action_config.json

There are two supported methods to implement a custom action:

  • ADB shell command
  • Action server

Your JSON config file can define multiple instances of each type of implementation.

ADB shell command

You can define a single button that is implemented by executing a single adb shell command. For example, the following JSON snippet defines a single button that launches a web page:

  "shell_command":"am start -a android.intent.action.VIEW -d",
      "title":"Web Page",

The fields are:

  • shell_command: The command to execute in adb shell when the button is pressed
  • button: A single button object with the following subfields:

Action server

Action servers allow more control over the behavior of your actions. An action server is a host binary that listens for button press events from WebRTC using a socket pair. WebRTC forwards the events to the action server, and then the action server decides how to implement the action.

Action servers allow more powerful control, such as maintaining state (such as for a toggleable event) or even running "meta-actions" such as killing the current device, launching more devices, or starting a screen-recording browser extension. The possibilities are limited only by what you decide to implement inside the host binary.

The following JSON snippet defines an action server that listens for events on two buttons:

      "title":"Quick Settings",
      "title":"Do Not Disturb",

The fields are:

  • server: The name of your host binary module
  • buttons: An array of buttons, with the same subfields as above

After updating the JSON config, append the name of the action server module to the Soong config build variable cvd_custom_action_servers. For example:

# Append to this variable exactly as shown here.
SOONG_CONFIG_cvd += custom_action_servers

# Append the name of your action server(s) to this variable.
SOONG_CONFIG_cvd_custom_action_servers += cuttlefish_example_action_server

Each action server host binary should perform the following steps:

  1. Accept a socket file descriptor number as the first and only program argument.

    This socket is created by launch_cvd using socketpair with domain AF_LOCAL, type SOCK_STREAM, and protocol 0.

  2. In a loop, attempt to read 128 bytes from the socket. These bytes contain button press events sent by the WebRTC client in the format command:state. command is as provided in the JSON config, and state is the button press state (down or up).

  3. Act on the incoming events to simulate the custom action.