Run the Android emulator on Docker using Android Emulator Container Scripts

This article is the 17th day article of Android Advent Calendar 2020.

Android Emulator Container Scripts introduced last year, but it is said that a newly built emulator container is experimentally provided this year, so this article uses this script to use the Docker container. We will introduce the procedure for launching the Android emulator with, connecting adb, executing tests, and operating the emulator launched from a web browser.

Continuous Testing with Android Emulator Containers Android Developers Blog / Continuous testing with new Android emulator tools

Execution environment

As of December 17, 2020, KVM is required as described in the README, and it does not work with Docker on Mac or Windows. When running on a cloud service, you need an environment where you can use EC2 bare metal instances, enable nested virtualization, and use KVM.

-Amazon EC2 / Bare Metal Instance -Enable Nested Virtualization for Compute Engine / VM Instances (https://cloud.google.com/compute/docs/instances/enable-nested-virtualization-vm-instances)

In addition, a generator (emu-docker command) is provided when creating the image by yourself, a Python interpreter is required for execution, and Node when performing remote streaming on the Web. You will need .js and npm.

Use a pre-built image

As mentioned above, a pre-built emulator container is now provided and hosted in a public repository, so you can specify it to run without building.

As of December 2020, the following is provided as an image that allows interaction with the emulator.

https://github.com/google/android-emulator-container-scripts/blob/master/REGISTRY.MD#available-images

Start container

docker run \
  -e ADBKEY="$(cat ~/.android/adbkey)" \
  --device /dev/kvm \
  --publish 8554:8554/tcp \
  --publish 5555:5555/tcp  \
  us-docker.pkg.dev/android-emulator-268719/images/30-google-x64:30.1.2

Specify adbkey (private key) for the environment variable of ADBKEY.

When adbkey performs USB debugging etc. on Android 4.2.2 or later, a dialog asking whether to accept the RSA key that allows debugging via this computer is displayed, but this key is used to protect the device. .. Normally, it is automatically generated under .android, but it is obtained from the specified location and passed to the environment variable.

Allow docker to use KVM with --device/dev/kvm and allow port 8554 and port 5555 to be accessible from the main machine on the same port with --publish. The 8554 is a gRPC port used for access from Android Studio and JavaScript, and the 5555 is an ADB port.

When the container starts up and logcat is output, it's done.

docker ps

CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS                        PORTS                                                                     NAMES
2591234e0901        f202bb98c138        "/android/sdk/launch…"   About a minute ago   Up About a minute (healthy)   0.0.0.0:5555->5555/tcp, 0.0.0.0:8554->8554/tcp, 0.0.0.0:32768->5554/tcp   zen_khorana

ADB over the network

https://developer.android.com/studio/command-line/adb

You can make an adb connection to the emulator on Docker by adb connect to localhost: 5555 on the main machine.

adb connect localhost:5555
adb devices

List of devices attached
localhost:5555	device

You can install apk with adb install and run tests with connectedAndroidTest.

./gradlew connectedAndroidTest

https://developer.android.com/studio/test/command-line

If you want to disconnect

adb disconnect

You can disconnect with.

script

A sample script of such a series of flows has been released, so it is a good idea to prepare a script and execute it with CI by referring to this.

Create your own image

There is a generator that creates an image like the one used above, and if you want to use an image or emulator that is not on the list, you can create it separately.

In order to use the various scripts provided, clone and bring the script to the main machine.

git clone [email protected]:google/android-emulator-container-scripts.git
cd android-emulator-container-scripts

Running configure.sh makes the emu-docker command available on python virtualenv (venv).

source ./configure.sh
emu-docker -h

usage: emu-docker [-h] [-v] {list,licenses,create,interactive,cloud-build} ...

List and create emulator docker containers (0+untagged.253.gbcb1f3f).

positional arguments:
  {list,licenses,create,interactive,cloud-build}
    list                list all the available the publicly available
                        emulators and system images.
    licenses            Lists all licenses and gives you a chance to accept or
                        reject them.
    create              Given an emulator and system image zip file, generates
                        a Docker image comprising complete environment in
                        which the Android Emulator runs. After the Docker
                        image is started up, interaction with the emulator is
                        made possible via port forwarding and ADB, or gRPC and
                        WebRTC.
    interactive         Interactively select which system image and emulator
                        binary to use when creating a docker container
    cloud-build         Create a cloud builder distribution. This will create
                        a distribution for publishing container images to a
                        GCE repository.This is likely only useful if you are
                        within Google.

optional arguments:
  -h, --help            show this help message and exit
  -v, --verbose         Set verbose logging (default: False)

You can see a list of available emulators and system images with emu-docker list.

SYSIMG K android x86 19 https://dl.google.com/android/repository/sys-img/android/x86-19_r06.zip
SYSIMG K google_apis x86 19 https://dl.google.com/android/repository/sys-img/google_apis/x86-19_r40.zip
SYSIMG L android x86 21 https://dl.google.com/android/repository/sys-img/android/x86-21_r05.zip
SYSIMG L google_apis x86 21 https://dl.google.com/android/repository/sys-img/google_apis/x86-21_r32.zip
SYSIMG L android x86 22 https://dl.google.com/android/repository/sys-img/android/x86-22_r06.zip
SYSIMG L google_apis x86 22 https://dl.google.com/android/repository/sys-img/google_apis/x86-22_r26.zip
SYSIMG M android x86 23 https://dl.google.com/android/repository/sys-img/android/x86-23_r10.zip
SYSIMG M google_apis x86 23 https://dl.google.com/android/repository/sys-img/google_apis/x86-23_r33.zip
SYSIMG N android x86 24 https://dl.google.com/android/repository/sys-img/android/x86-24_r08.zip
SYSIMG N google_apis x86 24 https://dl.google.com/android/repository/sys-img/google_apis/x86-24_r27.zip
SYSIMG N google_apis_playstore x86 24 https://dl.google.com/android/repository/sys-img/google_apis_playstore/x86-24_r19.zip
SYSIMG N android x86 25 https://dl.google.com/android/repository/sys-img/android/x86-25_r01.zip
SYSIMG N google_apis x86 25 https://dl.google.com/android/repository/sys-img/google_apis/x86-25_r18.zip
SYSIMG N google_apis_playstore x86 25 https://dl.google.com/android/repository/sys-img/google_apis_playstore/x86-25_r09.zip
SYSIMG O android x86_64 26 https://dl.google.com/android/repository/sys-img/android/x86_64-26_r01.zip
SYSIMG O android x86 26 https://dl.google.com/android/repository/sys-img/android/x86-26_r01.zip
SYSIMG O google_apis x86_64 26 https://dl.google.com/android/repository/sys-img/google_apis/x86_64-26_r16.zip
SYSIMG O google_apis x86 26 https://dl.google.com/android/repository/sys-img/google_apis/x86-26_r16.zip
SYSIMG O google_apis_playstore x86 26 https://dl.google.com/android/repository/sys-img/google_apis_playstore/x86-26_r07.zip
SYSIMG O android x86_64 27 https://dl.google.com/android/repository/sys-img/android/x86_64-27_r01.zip
SYSIMG O android x86 27 https://dl.google.com/android/repository/sys-img/android/x86-27_r01.zip
SYSIMG O google_apis x86 27 https://dl.google.com/android/repository/sys-img/google_apis/x86-27_r11.zip
SYSIMG O google_apis_playstore x86 27 https://dl.google.com/android/repository/sys-img/google_apis_playstore/x86-27_r03.zip
SYSIMG P android x86_64 28 https://dl.google.com/android/repository/sys-img/android/x86_64-28_r04.zip
SYSIMG P android x86 28 https://dl.google.com/android/repository/sys-img/android/x86-28_r04.zip
SYSIMG P google_apis x86_64 28 https://dl.google.com/android/repository/sys-img/google_apis/x86_64-28_r11.zip
SYSIMG P google_apis_playstore x86_64 28 https://dl.google.com/android/repository/sys-img/google_apis_playstore/x86_64-28_r08.zip
SYSIMG P google_apis_playstore x86 28 https://dl.google.com/android/repository/sys-img/google_apis_playstore/x86-28_r08.zip
SYSIMG P google_apis_playstore x86 28 https://dl.google.com/android/repository/sys-img/google_apis_playstore/x86-28_r09.zip
SYSIMG P google_ndk x86 28 https://dl.google.com/android/repository/sys-img/google_ndk/x86-28_r10.zip
SYSIMG Q google_apis x86_64 29 https://dl.google.com/android/repository/sys-img/google_apis/x86_64-29_r11.zip
SYSIMG Q google_apis x86 29 https://dl.google.com/android/repository/sys-img/google_apis/x86-29_r11.zip
SYSIMG R google_apis x86_64 30 https://dl.google.com/android/repository/sys-img/google_apis/x86_64-30_r09.zip
SYSIMG R google_apis x86 30 https://dl.google.com/android/repository/sys-img/google_apis/x86-30_r09.zip
EMU stable 30.2.6 macosx https://dl.google.com/android/repository/emulator-darwin-6962233.zip
EMU stable 30.2.6 linux https://dl.google.com/android/repository/emulator-linux-6962233.zip
EMU stable 30.2.6 windows https://dl.google.com/android/repository/emulator-windows-6962233.zip
EMU stable 28.0.25 windows https://dl.google.com/android/repository/emulator-windows-5395263.zip
EMU canary 30.3.4 macosx https://dl.google.com/android/repository/emulator-darwin-7020230.zip
EMU canary 30.3.4 linux https://dl.google.com/android/repository/emulator-linux-7020230.zip
EMU canary 30.3.4 windows https://dl.google.com/android/repository/emulator-windows-7020230.zip

SYSIMG is the system image and EMU is the emulator. Download what you need for each

curl -O 'https://dl.google.com/android/repository/emulator-linux-7020230.zip'
curl -O 'https://dl.google.com/android/repository/sys-img/google_apis/x86_64-30_r09.zip'

By executing emu-docker create <emulator.zip> <sysimg.zip>, necessary files such as DockerFile will be placed under src.

emu-docker create emulator-linux-7020230.zip sys-img-google_apis-30-R-x86_64.zip

src/DockerFile


# Copyright 2019 - The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
FROM debian:stretch-slim AS emulator

# Install all the required emulator dependencies.
# You can get these by running ./android/scripts/unix/run_tests.sh --verbose --verbose --debs | grep apt | sort -u
# pulse audio is needed due to some webrtc dependencies.
RUN apt-get update && apt-get install -y --no-install-recommends \
# Emulator & video bridge dependencies
    libc6 libdbus-1-3 libfontconfig1 libgcc1 \
    libpulse0 libtinfo5 libx11-6 libxcb1 libxdamage1 \
    libnss3 libxcomposite1 libxcursor1 libxi6 \
    libxext6 libxfixes3 zlib1g libgl1 pulseaudio socat \
# Enable turncfg through usage of curl
    curl ca-certificates && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# Now we configure the user account under which we will be running the emulator
RUN mkdir -p /android/sdk/platforms && \
    mkdir -p /android/sdk/platform-tools && \
    mkdir -p /android/sdk/system-images/android && \
    mkdir -p /android-home

# Make sure to place files that do not change often in the higher layers
# as this will improve caching.
COPY launch-emulator.sh /android/sdk/
COPY platform-tools/adb /android/sdk/platform-tools/adb
COPY default.pa /etc/pulse/default.pa

RUN gpasswd -a root audio && \
    chmod +x /android/sdk/launch-emulator.sh /android/sdk/platform-tools/adb

COPY emu/ /android/sdk/
COPY avd/ /android-home
COPY sys/ /android/sdk/system-images/android/
# Create an initial snapshot so we will boot fast next time around,
# This is currently an experimental feature, and is not easily configurable//
# RUN --security=insecure cd /android/sdk && ./launch-emulator.sh -quit-after-boot 120

# This is the console port, you usually want to keep this closed.
EXPOSE 5554

# This is the ADB port, useful.
EXPOSE 5555

# This is the gRPC port, also useful, we don't want ADB to incorrectly identify this.
EXPOSE 8554

ENV ANDROID_SDK_ROOT /android/sdk
ENV ANDROID_AVD_HOME /android-home
WORKDIR /android/sdk

# You will need to make use of the grpc snapshot/webrtc functionality to actually interact with
# the emulator.
CMD ["/android/sdk/launch-emulator.sh"]

# Note we should use gRPC status endpoint to check for health once the canary release is out.
HEALTHCHECK --interval=30s \
            --timeout=30s \
            --start-period=30s \
            --retries=3 \
            CMD /android/sdk/platform-tools/adb shell getprop dev.bootcomplete | grep "1"

# Date frequently changes, so we place this in the last layer.
LABEL maintainer="ntsk@linux" \
      SystemImage.Abi=x86_64 \
      SystemImage.TagId=google_apis \
      SystemImage.GpuSupport=true \
      AndroidVersion.ApiLevel=30 \
      com.google.android.emulator.description="Pixel 2 Emulator, running API 30" \
      com.google.android.emulator.version="google_apis-30-x86_64/30.3.4"

You can start it as you did the first time with docker build-> docker run.

docker build src
...
...
Successfully built 809a82460bb3
docker run -e ADBKEY="$(cat ~/.android/adbkey)" \                                                                                                                                       + master 
--device /dev/kvm \                                                                                                                                                                                                                          
--publish 8554:8554/tcp \                                                                                                                                                                                                                    
--publish 5555:5555/tcp 809a82460bb3

For docker run, there is a wrapper script in run.sh, so you can use that as well. https://github.com/google/android-emulator-container-scripts/blob/master/run.sh

In fact, there is an option to do the contents up to this point interactively, and by executing this, the image will be created interactively, and by specifying —start, it will even start.

emu-docker interactive --start

Screenshot from 2020-12-16 21-31-35.png Screenshot from 2020-12-16 21-32-09.png

Remote streaming on the web

A sample that enables access via the Web is prepared, and by using docker-compose, you can launch the following four containers and perform Web access to the emulator.

Envoy is used as an edge proxy or service proxy, and the React application running on Nginx draws the emulator with WebRTC. It has become. It also comes with a simple basic authentication sample using JWT.

js/docker-compose.yaml


version: "3.7"
services:
  front-envoy:
    image: emulator_envoy:latest
    container_name: emulator_envoy
    networks:
      - envoymesh
    expose:
      - "8080"
      - "8001"
      - "8443"
    ports:
      - "80:8080"
      - "443:8443"
      - "8001:8001"

  emulator:
    image: emulator_emulator:latest
    container_name: emulator_emulator
    networks:
      envoymesh:
        aliases:
          - emulator
    devices: [/dev/kvm]
    shm_size: 128M
    expose:
      - "8554"

  jwt_signer:
    image: emulator_jwt_signer:latest
    container_name: emulator_jwt_signer
    networks:
      envoymesh:
        aliases:
          - jwt_signer
    expose:
      - "8080"

  nginx:
    image: emulator_nginx:latest
    container_name: emulator_nginx
    networks:
      envoymesh:
        aliases:
          - nginx
    expose:
      - "80"

networks:
  envoymesh: {}

As with the previous item, create a container in advance using the emulator and system image.

emu-docker create emulator-linux-7020230.zip sys-img-google_apis-30-R-x86_64.zip

After creating, run ./create_web_container.sh. By passing the user id and password with the -p option, the token service is configured according to the user, and the key pair used for encryption/decryption of the JWT token is generated. Node.js, npm is required for execution.

./create_web_container.sh -p user,password

Specify js/docker/docker-compose.yml and docker-compose up.

docker-compose -f js/docker/docker-compose.yaml up

If you want to use adb, also specify js/docker/development.yaml as follows.

docker-compose -f js/docker/docker-compose.yaml -f js/docker/development.yaml up
docker ps

CONTAINER ID        IMAGE                        COMMAND                  CREATED              STATUS                        PORTS                                                                            NAMES
3c9dda3ce35c        emulator_jwt_signer:latest   "python jwt-provider…"   About a minute ago   Up About a minute             8080/tcp                                                                         emulator_jwt_signer
d8537ef89c1a        emulator_emulator:latest     "/android/sdk/launch…"   About a minute ago   Up About a minute (healthy)   5554-5555/tcp, 8554/tcp                                                          emulator_emulator
49f29b2717d1        emulator_envoy:latest        "/docker-entrypoint.…"   About a minute ago   Up About a minute             0.0.0.0:8001->8001/tcp, 10000/tcp, 0.0.0.0:80->8080/tcp, 0.0.0.0:443->8443/tcp   emulator_envoy
b1eec8500916        emulator_nginx:latest        "nginx -g 'daemon of…"   About a minute ago   Up About a minute             80/tcp                                                                           emulator_nginx

When you connect to localhost, the login screen will be launched, so log in with the id/password specified at the time of creation.

Screenshot from 2020-12-16 22-12-23.png

After logging in, you can operate the emulator on the browser.

Screenshot from 2020-12-16 22-13-48.png Screenshot from 2020-12-16 22-15-53.png

The emulator drawing is displayed in real time using WebRTC if a P2P connection can be created to the server hosting the emulator, but if not available it seems to create an image every second and display it in the browser ( Performance is low).

Summary

When launching the emulator on CI and testing, Firebase Test Lab and AWS Device Farm exist as tools that can be used casually, but refer to these samples. It seems that in-house production of CI environment using emulator will become easier.

I am very grateful that Google provided a sample for building such a CI environment, and I will continue to check the trends.

Recommended Posts

Run the Android emulator on Docker using Android Emulator Container Scripts
Run React on a Docker container
Run the AWS CLI on Docker
Run GUI application on Docker container
Run PureScript on a Docker container
Try using the service on Android Oreo
Send emails using Docker container on Raspberry Pi 3
Run phpunit on Docker
[Introduction] Display Android Studio Hello World on the emulator
[Docker] Build an Apache container on EC2 using dockerfile
Run VS Code on Docker
Run openvpn on Docker (windows)
Decomposing the Docker run command. .. ..
Time is wrong with the application launched on the Docker container
Try Hello World using plain Java on a Docker container
Run SSE (Server-Sent-Event) samples on docker
Steps to run docker on Mac
Run puppeteer-core on Heroku (Docker edition)
[Microsoft] Run Azure Pipelines (VSTS) Agent on Docker or Azure Container Instance
Try using Redmine on Mac docker
[Android] Get the date on Monday
Save ArrayList using GSON on Android
Install Ubuntu20.04 on RaspberryPi 4 and build Kubernetes to run the container
Using Docker on Windows10 Home WSL2
Run x11 apps in a Docker container (supports network access from the container)
[Docker] How to build when the source code is bind-mounted on the container
Let's specify the version when using docker
Launch docker container on EC2 (personal memorandum)
Run Embulk on Docker to convert files
The story of updating SonarQube's Docker Container
Quick build maven project using maven docker container
[Docker] Start the container as soon as possible
Try using the Emotion API from Android
Try the Docker environment on AWS ECS
Until you run apache on ubuntu on docker
Run Ubuntu + ROS with Docker on Mac
Let's run batch in container using Azure Batch
Check when the container cannot be accessed from Host in the Laravel on docker environment using VS Code's Remote container.
Up to the point of launching a Docker container built using RedHat Quarkus
Monitor the Docker container and SystemD process on the same host with Zabbix on Ubuntu.
Run Docker environment Rails MySQL on Heroku. devise and hiding the twitter API
A quick note on using jshell with the official Docker image of the JDK
Try using Firebase Cloud Functions on Android (Java)
Using templates on the classpath with Apache Velocity
I tried using Docker for the first time
[Android] Get the tapped position (coordinates) on the screen
How to check the logs in the Docker container
Using JUnit from the command line on Ubuntu
[Docker] Check the running container and enter there
Extract key phrases using Text Analytics Docker container
[Android] List all setting items on the setting screen
Android application development using Unity + ARCore on Ubuntu
Build a Kotlin app using OpenJDK's Docker container
Tips for using the Spotify app on Ubuntu
Run JSP Hello World with Tomcat on Docker
I made a Docker container to run Maven
Translator using Microsoft Translator Text API on Android ~ Implementation ~
Update container image with KUSANAGI Runs on Docker
I tried running Ansible on a Docker container
Compile and run Java on the command line
Try communication using gRPC on Android + Java server