First, install docker and git, and modprobe vhost_vsock and vhost_net:
curl https://get.docker.com | sh
sudo apt install -y git
sudo modprobe vhost_vsock vhost_net
Then the Docker image can be built as follows:
git clone https://github.com/google/android-cuttlefish.git
cd android-cuttlefish/docker
./build.sh
Set up your environment:
Option 1: Use setup.sh
:
source setup.sh # once in each terminal window
Option 2: Add the cf
script in this directory to your $PATH
.
Then any command beginning cf_foo
listed below may be invoked as
cf foo
. Any references to $ip_foo
must be replaced with cf get_ip foo
. This option is ideal if you don't want to source the script
in every terminal window, or if you use a shell other than bash.
Create a container:
cf_docker_create <name> # <name defaults to cuttlefish>
Note that a few options are provided in creating the container:
cf_docker_create -h
The options include but are not limited to:
- pull the host packages and images from a host directory
- mount the host directories to the docker container: log directory, .gitconfig, etc
- run the docker container, log in once, then delete it on logout
- have the guest applications (e.g. Cuttlefish) use the host X server
cf_docker_rm <name> # <name defaults to cuttlefish>
To list the Cuttlefish containers:
cf_docker_list
There are three ways to provision a container (that is, to install the Cuttlefish and Android images on it.)
If cf_docker_create detects an Android build environment that is set up to build Cuttlefish, it will automatically set up the container to point to all the binary artifacts. Assuming you have downloaded, set up, and built a Cuttlefish device and Android from the sources, and you call cf_docker_create in the same terminal, you can jump ahead to launching Cuttlefish by adding -A -C option to cf_docker_create
cf_docker_create -A -C <other options> <name>
If you do not have the Android build environment, alternatively, you can build Android and Cuttlefish with build-android-with-docker.sh. This is a wrapper for another docker container dedicated to build Cuttlefish and Android from the source.
The source could be downloaded by following the instructions here. Once repo sync is done, the source tree is ready for build.
Say, the source is downloaded to $HOME/android-src. The following command will build the Android and Cuttlefish from the source.
./build-android-with-docker.sh --android_src_mnt "$HOME/android-src:/home/vsoc-01/build"
For more detail:
./build-android-with-docker.sh --help
The build-android-with-docker.sh script also allows:
- running a docker guest command with arguments
- mounting a host script at docker container to run it with arguments
- passing another set of arguments to docker run at the same time
- as a showcase, just init, sync, and build with default configurations
Note that if arguments should be passed to the guest command or the host script, it should be done like this:
./build-android-with-docker.sh --android_src_mnt "$HOME/android-src:/home/vsoc-01/build" \
--op_mode=guest guest_program -- -a -b -c and more arguments
-- makes the following arguments like -a, -b, -c, arguments, rather than options to the ./build-android-with-docker script. Without --, getopt that internally implements cmdline parsing will be confused.
Also, for --docker_run_opts, spaces are not allowed. Thus, for a short docker option with optional parameter, you should do as follows:
-vVALUE1:VALUE2
For a long docker run option with optional parameter, you should do like this:
--name=MYNAME
In summary, a docker_run_opts looks like:
--docker_run_opts="-vV1:V2,--name=MYNAME,--privileged,-a,-b"
Although the command will build Android and Cuttlefish, the docker contaienr does not automatically detect the directory. To provision the docker container with the freshly built Android and Cuttlefish, we must give -C and -A options to specify the location of cvd-host_package.tar.gz and the ANDROID_PRODUCT_OUT directory, respectively.
Assuming that cvd-host_package.tar.gz file is in $HOME/android-src/out/host/linux-x86 and the ANDROID_PRODUCT_OUT is $HOME/android-src/out/target/product/vsoc_x86, the command would look like this:
cf_docker_create -C $HOME/android-src/out/host/linux-x86 -A
$HOME/android-src/out/target/product/vsoc_x86 <other options> <name>
Alternatively, you can manually set the two environment variables, and give -A -C with default values:
export
ANDROID_PRODUCT_OUT=$HOME/android-src/out/target/product/vsoc_x86
export ANDROID_HOST_OUT=$HOME/android-src/out/host/linux-x86
cf_docker_create -A -C $HOME/android-src <name>
If you you want to provision the container with a prebuilt cuttlefish image for example, from AOSP CI, you can follow the steps to obtain a prebuilt image of Cuttlefish (start from step #2, since the container already took care of it). Once you've obtained the image at step #8, you can copy it to your container as follows:
# We assume the container is called 'cuttlefish' and
# it was created with "cf_docker_create cuttlefish"
ssh vsoc-01@${ip_cuttlefish} -- 'tar xzvf -' < cvd-host_package.tar.gz
scp *.img vsoc-01@${ip_cuttlefish}:~/
As an aid, if you would like to fetch the latest Cuttlefish build from AOSP:
ssh vsoc-01@${ip_cuttlefish} -- './download-aosp -A -C -a $(uname -m)'
-A and -C respectively enable the download of Android images and the host package. -a indicates the architecture.
Note that if you want to run Cuttlefish built for x86, you will probably have to invoke ./download-aosp with the arguments -a 'x86', since your machine will most likely be 'x86_64':
ssh vsoc-01@${ip_cuttlefish} -- './download-aosp -A -C -a x86'
To launch Cuttlefish within the container you've created:
cf_start_<name> options
For example, if you want to create acontainer named 'cf1' and launch Cuttlefish with WebRTC support, 4GB of RAM and 4 CPUs:
source setup.sh
cf_docker_create cf1
ssh vsoc-01@$ip_cf1 -- './download-aosp $(uname -m)'
cf_start_cf1 --start_webrtc --cpus 4 --memory_mb 4096
As of now, cuttlefish is connecting the adb server inside the docker container by default. If it is intended to adb running outside, --norun_adb_connector should be given and connected manually:
cf_start_cf --norun_adb_connector --start_webrtc --cpus 4 --memory_mb 4096
Following that, open a(nother) terminal, and type:
adb connect ip_cf1:6520
Once done, you stop cuttlefish as follows (in a new terminal)
source setup.sh
cf_stop_cf1
Each container's IP address will be saved in variable $ip_, where is the name of the container. In the example above, the IP address would be in variable $ip_cf1.
We recommend that each docker container should be assigned an ip address and that the users should access the contaienr via the ip.
If assigning a public IP is not feasible, we offer a environment variable, ip_${name}
and a bash function, cf_get_ip
.
Those returns the internal IP valid on the docker host.
As the internal ip returned by cf_get_ip
and also stored in
ip_${name}
is not recognized outside the docker host, if remote
access to the container is desired, extra steps are required. It is
described in the IPvlan and Port Forwarding
sections.
When the adb client is running outside the container either remotely or locally (on the docker host), the cuttlefish inside the container could be accessed as follows:
adb connect $ip_cf1:6520
adb shell
Please note that --norun_adb_connector should have been given to the cf_start_ command. Also, $ip_cf1
is the public or internal
ip address of the container.
Alternatively, you could run adb inside the container, and use the adb client over ssh:
ssh vsoc-01@$ip_cf1 -- ./bin/adb -e shell
You can connect to cuttlefish via webRTC. you can
connect (on the same machine as the docker container) by pointing your browser
at https://$container_ip:8443/
WebRTC allows you to control cuttlefish inside the docker container without ssh tunnel.
Please note that now WebRTC is the default. Also, please note that we have tested WebRTC with Google Chrome Browsers, not with Internet Explorers, Safari, Firefox, Opera, etc.
Simply, from a web browser:
http://192.168.9.2:8443
Please note that you should replace 192.168.9.2
with the actual IP address of your container.
Alternatively, you can use the Google Chrome inside the container. Chrome will behave like an X application running directly on the docker host. The idea is that we forward all X requests from the Chrome to the host X server as opposed to the X server insied the container. Please note that you may have to close the Chrome already running on the host.
For that, the first step is to create the container with -x option:
cf_docker_create -x -A -C $HOME/android-src cf1
Once cuttlefish is launched, the following command from another terminal will launch Google Chrome:
source setup.sh
cf_login_cf1 google-chrome-stable
This Google Chrome renders on the host but runs inside the container. Thus, use the following IP address and port to access to cuttlefish:
https://127.0.0.1:8443
Note that 127.0.0.1 is not meant to be the docker host.
We do not believe ssh tunneling works smoothly for WebRTC. WebRTC uses tcp and udp ports. Not sure if UDP ports are for ssh tunneling.
You can expose the container directly on a LAN by creating an IPvlan, which is the recommended approach to avoid WebRTC issues associated with port forwarding. This effectively creates an additional IP address for your host that gets routed to the container. For example, to create an IPvlan that assigns the IP address 192.168.1.123 to your container, use something like the following command:
docker network create -d ipvlan --subnet=192.168.1.0/24 --gateway=192.168.1.1 --ip-range=192.168.1.123/32 -o parent=eth0 myvlan
See the Docker documentation for details. Once you've done this, you can specify the name of the IPvlan when creating the container:
cf_docker_create --network myvlan
Make sure not to expose this IP address to the Internet, as Cuttlefish
will allow passwordless authentication as the user vsoc-01
on the
SSH port.
The assumption here is an IP address cannot be assigned to the docker container. Likely, the container should be accessed remotely: from a client outside the docker host. Please let us remind the readers that it's not what we recommend.
One way to do that is to use a bunch of -p options to expose the port. In setup.sh
, there is a single docker run
command. Feel free to add a bunch of -p options to exports host ports to the docker container.
Alternatively, we offer an optional, convenience function: cf_publish_cf1
where cf1
is the name of the container.
The function exposes a set of host ports to the docker container. Internally, it uses socat
. As an example, by default,
the adb port is 6520. Each container is automatically assigned a unique id
when cf_docker_create
creates it.
cf_publish_cf1
maps the host port, 6520 + id
, to the docker container's adb port.
The cf_get_instance_id
bash function returns the id
:
$ cf_get_instance_id "cf1"
0
For WebRTC, this may not work well. WebRTC wants to make sure that the server IP address that the client sends the request to should match the IP the server is actually running. With this socat-based forwarding scheme, the client will sends the http(s) requests to the docker host IP. However, the WebRTC server is actually running inside the container.