KDEConnect/Android Emulator
Running KDE Connect in an Android Emulator
The Android emulator uses the default qemu User Networking (SLIRP) backend which does not allow the emulator to be directly accessible from the host nor the local network, which prevents kdeconnect running on the desktop from seeing kdeconnect running in an emulator. In order to get a fully working network in the emulator it needs to be configured to use the tap networking backend which as an added bonus offers much better performance than the default user backend.
Running the emulator with the tap backend will create a virtual network device on the host machine that can then be configured as if it is a real ethernet card allowing you to create virtually any type of network topology. The most straightforward topology that can be created is the public bridge setup. In this setup we simply put both the emulators virtual interface and the desktops lan interface into a bridge so they will share the same network space (e.g. single IP-subnet). Another topology that can be used is the Routing with iptables setup this involves a little more work but will work in situations where your lan device does not support bridging (eg. some wireless drivers)
The public bridge setup
Setting up your system and the android emulator to use the public bridge setup requires changes on both sides. These changes are described in the following sections.
Configuring the host machine
First you will have to unconfigure your lan interface, create the bridge, add your lan interface to it and assign the bridge an ip address. Assuming your lan interface is eth0 and the bridge interface you want to create is br0:
sudo ifconfig eth0 0.0.0.0 sudo ip link add br0 type bridge sudo ip link set eth0 master br0 dhclient br0
To make the above changes permanent do the following: taken from kvm docs and reboot your system
System | Changes |
---|---|
RedHat/Fedora |
DEVICE=br0 BOOTPROTO=dhcp ONBOOT=yes TYPE=Bridge STP=no ZONE=<the same zone as eth0 is in> |
Debian | Edit /etc/network/interfaces
# Replace old eth0 config with br0 auto # Use old eth0 config for br0 and configure the bridge iface br0 inet dhcp bridge_ports eth0 bridge_stp off bridge_maxwait 0 bridge_fd 0 |
SuSE |
|
Next you have to allow qemu-bridge-helper to add tap interfaces to this bridge, using your favorite text editor:
sudo vi /etc/qemu/bridge.conf
- and add the following line:
allow br0
You can now start the emulator using the following command replacing <avd_name> with the name of an existing emulator:
emulator -avd <avd_name> -qemu --device virtio-net-pci,netdev=n1 -netdev tap,id=n1,"helper=/usr/libexec/qemu-bridge-helper --br=br0"&
To list available devices to use as <avd_name> use:
emulator -list-avds
If you want to run more than 1 emulator at a time you will also have to provide a mac address to be used by the emulator
MAC=`printf '52:54:00:12:%02X:%02X\n' $((RANDOM%256)) $((RANDOM%256))` emulator -avd <avd_name> -qemu --device virtio-net-pci,netdev=n1,mac=${MAC} -netdev tap,id=n1,"helper=/usr/libexec/qemu-bridge-helper --br=br0"&
Configuring the android emulator
Now that we have the host system setup its time to make things work in the emulator.
First put Android in airplane mode by dragging down the notification bar and pressing the airplane icon
Then get a root shell in the emulator
adb -s emulator-5554 shell su
Next start a dhcpclient on eth1 to configure the interface
For an emulator with API level > 24 issue
daemonize dhcpclient -i eth1
For an emulator with API Level > 22 and <= 24 issue
dhcptool eth1
For anything older issue
mkdir /data/misc/dhcp dhcpcd -b eth1
Make normal routing take precedent over anything that google/qemu configured. If ip does not work use busybox ip
instead
ip rule add from all lookup main pref 99
Almost there
Take android out of airplane mode and for API levels > 24 wait until wlan0 is up again
For an emulator with API level > 24 make dns lookups work again by issueing
ip ro add 10.0.2.3 via 192.168.232.1 dev wlan0
And for an emulator with API level <= 23 delete the default route sending everything through the SLIRP interface.
ip ro del default via 10.0.2.2
You should be able to figure out what to do if the ip command does not exits
Congratulations you are now the proud owner of an Android emulator with proper networking
And then there was a script to do most of the hard work for you
Because like me you probably don't want to do that every day I've create a little script to do all the hard work for you.
#!/bin/sh # Copyright 2018 Erik Duisters # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of # the License or (at your option) version 3 or any later version # accepted by the membership of KDE e.V. (or its successor approved # by the membership of KDE e.V.), which shall act as a proxy # defined in Section 14 of version 3 of the license. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # Tested with emulators API 28-15 (excl 20) # API 15 has ash to [[ ]] does not work but networking seems to come up like it should if [ -z "$1" ] then echo "Error: No avd specified use one of" emulator -list-avds exit 1 fi #https://www.linux-kvm.org/page/Networking #https://serverfault.com/questions/308091/set-mac-address-in-qemu-kvm-properties-file MAC=`printf '52:54:00:12:%02X:%02X\n' $((RANDOM%256)) $((RANDOM%256))` emulator -avd $1 -qemu -device virtio-net-pci,netdev=n1,mac=$MAC -netdev tap,id=n1,"helper=/usr/libexec/qemu-bridge-helper --br=br0"& sleep 5 #TODO: Need a better way to get the emulator number EMULATOR=`adb devices | grep emulator | tail -n1 | cut -f 1` echo "Waiting until $EMULATOR boot is complete" adb -s $EMULATOR wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done;' echo "Boot complete - Configuring bridge mode networking" sleep 5 SDK="" while [[ -z $SDK ]] do SDK=`adb -s $EMULATOR shell getprop ro.build.version.sdk | sed 's/.*\([0-9][0-9]\).*/\1/'` done echo "Emulator has sdk version: ${SDK}" IP="ip" if (($SDK == 18)) then IP="busybox ip" fi echo "Turning airplane mode on" adb -s $EMULATOR shell "su 0 settings put global airplane_mode_on 1" adb -s $EMULATOR shell "su 0 am broadcast -a android.intent.action.AIRPLANE_MODE" if (($SDK > 24)) then echo "Waiting for wlan0 to go down" while ! `adb -s emulator-5554 shell "su 0 ip link show wlan0" | grep -q "state DOWN"`; do sleep 2; done fi echo "Starting dhcpclient for eth1" if (($SDK > 24)) then echo "using dhcpclient" adb -s $EMULATOR shell "su 0 daemonize dhcpclient -i eth1" elif (($SDK > 22)) then echo "using dhcptool" adb -s $EMULATOR shell "su 0 dhcptool eth1" else echo "using dhcpcd" if (($SDK <= 16)) then mkdir /data/misc/dhcp fi adb -s $EMULATOR shell "su 0 dhcpcd -b eth1" fi adb -s $EMULATOR shell "su 0 ${IP} rule add from all lookup main pref 99" sleep 2 echo "Turning airplane mode off" adb -s $EMULATOR shell "su 0 settings put global airplane_mode_on 0" adb -s $EMULATOR shell "su 0 am broadcast -a android.intent.action.AIRPLANE_MODE" if (($SDK > 24)) then echo "Waiting for wlan0 to come UP" while ! `adb -s emulator-5554 shell "su 0 ip link show wlan0" | grep -q "state UP"`; do sleep 2; done sleep 2 echo "Re-enabling dns lookups" adb -s $EMULATOR shell "su 0 ip ro add 10.0.2.3 via 192.168.232.1 dev wlan0" elif (($SDK <= 23)) then adb -s $EMULATOR shell "su 0 ${IP} ro del default via 10.0.2.2" fi echo "Configuration complete"
The routing with iptables setup
Configuring the host machine
Since you do not want to run the emulator as the super user you will have to grant normal users permission to use tap devices.
- Create a net udev rule that creates the net/tun device file with the needed permission
sudo vi /etc/udev/rules.d/00-net-tun.rules
- The contents should be:
KERNEL=="tun", GROUP="netdev", MODE="0660", OPTIONS+="static_node=net/tun"
- Create a new group netdev and add yourself to it
sudo groupadd netdev sudo usermod -a -G netdev `whoami`
- Now reboot your system so the changes take effect
Configure the host system to support the routed setup
- Create and bring up a new tap device
sudo ip tuntap add tap0 mode tap user `whoami` sudo ip link set tap0 up
- Allow ip forwarding
sudo sysctl -w net.ipv4.ip_forward=1
- Allow proxy_arp so the host will respond to "who has" arp messages for the emulator
sudo sh -c "echo 1 > /proc/sys/net/ipv4/conf/all/proxy_arp"
- Route all traffic for the emulator through the tap device (ps. use an address in your subnet)
sudo ip ro add 192.168.0.100 dev tap0
- Allow traffic to and from the emulator (you can use tap+ instead of tap0 to allow all tap devices)
sudo iptables -I FORWARD 1 -i tap0 -j ACCEPT sudo iptables -I FORWARD 1 -o tap0 -j ACCEPT sudo iptables -I INPUT 1 -i tap0 -j ACCEPT sudo iptables -I OUTPUT 1 -o tap0 -j ACCEPT
- Time to start the emulator
emulator -avd <avd-name> -qemu --device virtio-net-pci,netdev=n1 -netdev tap,id=n1,ifname=tap0,script=no,downscript=no
Configuring the android emulator
Now that the host is configured a couple of things need to be configured in the emulator
- Start a shell in the emulator and become root
adb -s emulator-5554 shell su
- Assign an ip-address to the tap device
ifconfig eth1 192.168.0.100
- Configure the default gateway (use the address of your desktop's lan interface here)
ip ro add default via 192.168.0.2
- Make normal routing take precedent over anything that google/qemu configured
ip rule add from all lookup main pref 99
- Make dns lookups work again
ip ro add 10.0.2.3 via 192.168.232.1 dev wlan0
Have fun: remember broadcasts do not work in this setup so you will have to configure the ip address of the host machine in kdeconnect on the emulator (overflow menu->Add devices by IP>