Rooting Intel Android Devices on Linux/Mac OS X

In this article I will explain how to root Intel-CPU based android Devices on Linux/Mac OS X. The instructions are based on this forum post. I used this code to root a Medion LIFETAB P8912. However this should also apply to all devices mentioned in this forum post.

The first thing to do, is install fastboot and adb on your PC/Mac. Make sure that you have enabled the development option on your android device and are able to connect to it via adb.

Then place the update, you want to install on the sdcard on your device. In case you want to install the root patch, you can download the latest SuperSU. Note that you will be not able to install custom roms, if your bootloader is locked. If the signature missmatch it will refuse to boot.

The next thing to do is to download and extract IntelAndroid-FBRL-07-24-2015.7z mentioned in the post. It contains a recovery images for CWM or TWRP and some custom trigger code to start a temporary CWM Recovery Session on the device. After reboot this session will be gone. But you can apply updates during the session such as SuperSU. You will not be able to follow the exact instructions from this forum post, because it contains a windows specific batch file and windows executables. However these are just fancy wrappers around adb and fastboot, so you can still use the contained images/launch code.

To reboot your device into the bootloader, connect it to your computer and run, while it is turned on:

1
$ adb reboot-bootloader

Within the boot loader, we will first put the alternate rescue image on the device along with some custom launcher code. I first tried TWRP on my device, but my touchscreen didn’t work with it, so I sticked to CWM:

1
2
3
# assuming you have changed to the directory of extracted archive:
$ fastboot flash /tmp/recovery.zip FB_RecoveryLauncher/cwm.zip
$ fastboot flash /tmp/recovery.launcher FB_RecoveryLauncher/recovery.launcher

The next thing to do is to trigger the device via fastboot to start our recovery. The forum post contained 4 alternatives approaches based on the android device. The following (T4) was working for me:

1
$ fastboot oem start_partitioning; fastboot flash /system/bin/logcat FB_RecoveryLauncher/fbrl.trigger; fastboot oem stop_partitioning

This temporary replace logcat with a launcher. It is important to execute all commands in one shot. Otherwise fastboot will fail to flash logcat.

If the command will not work for you, you could one of these commands:

1
2
3
4
5
6
# T1
$ fastboot flash /sbin/adbd FB_RecoveryLauncher/fbrl.trigger; fastboot oem startftm
# T2
$ fastboot flash /system/bin/cp FB_RecoveryLauncher/fbrl.trigger; fastboot oem backup_factory
# T3
$ fastboot flash /sbin/partlink FB_RecoveryLauncher/fbrl.trigger; fastboot oem stop_partitioning

If everything works it should start the recovery image.

CGI-Like Python Scripts With Systemd Socket Activation

Lets say you want to trigger remote the start of a python script. But you don’t want to have a service running all the time waiting for requests.

What you can do, is using socket-unit in systemd, which is waiting on a tcp port for connections and starts the service, if somebody is requesting it.

The systemd configuration could look like this:

  • Listens on tcp port 3000 (both ipv4 and ipv6)
  • Execute python script as user ‘nobody’ with a timeout of 5 minutes
1
2
3
4
5
6
7
8
9
10
11
[Unit]
Description=Start update on demand

[Socket]
ListenStream=3000
# only listen on localhost
#ListenStream=127.0.0.1:3000
BindIPv6Only=both

[Install]
WantedBy=multi-user.target
1
2
3
4
5
6
7
[Unit]
Description=Start update on demand
JobTimeoutSec=5min

[Service]
User=nobody
ExecStart=/usr/bin/python /path/to/script.py

In your python code, do the following

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def systemd_socket_response():
    """
    Accepts every connection of the listen socket provided by systemd, send the
    HTTP Response 'OK' back.
    """
    try:
        from systemd.daemon import listen_fds;
        fds = listen_fds()
    except ImportError:
        fds = [3]

    for fd in fds:
        import socket
        sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(0)

        try:
            while True:
              conn, addr = sock.accept()
              conn.sendall(b"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 3\r\n\r\nOK\n")
        except socket.timeout:
            pass
        except OSError as e:
            # Connection closed again? Don't care, we just do our job.
            print(e)

if __name__ == "__main__":
   if os.environ.get("LISTEN_FDS", None) != None:
        systemd_socket_response()
   # here your own code begins
   do_work()

This still lacks of authentication and does not take any arguments. You could protect this port using a frontend webserver with http authentication, or you pass the listen socket to an python http server, which add some token passed authentication. Systemd will ensure, that your service will not run more than once at the time.

Fix Sshd Socket Activation When Using Tmux/screen

When using sshd.socket to start sshd on demand, detaching from a tmux/screen session will not work. The reason is once the ssh session is closed, systemd will terminate all remaining processes in the sshd cgroups, which affects also the tmux/screen background process. However this behaviour can be changed using the following drop-in file:

/etc/systemd/system/sshd@.service.d/killmode.conf
1
2
[Service]
KillMode=process

Network Configuration on Digitalocean for Freebsd

By default Digitalocean add some custom rc.d scripts for network configuration to your droplet.

You can just append the content of /etc/rc.digitalocean.d/droplet.conf to your /etc/rc.conf In my case the public ipv4 address is 188.166.0.1 and my first ipv6 address is 2a03:b0c0:2:d0::2a5:f001.

/etc/rc.conf
1
2
3
4
5
defaultrouter="188.166.0.1"
# ipv6 address are shortend for readability
ipv6_defaultrouter="2a03:b0c0:2:d0::1"
ifconfig_vtnet0="inet 188.166.16.37 netmask 255.255.192.0"
ifconfig_vtnet0_ipv6="inet6 2a03:b0c0:2:d0::2a5:f001 prefixlen 64"

Digitalocean provides these days for native Ipv6 for the most of its datacenters. Unlike other hoster they are very spare, when distributing Ipv6 Addresses and only route 16 addresses per droplet (xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxx1 until xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxf). To make use of these additional ip addresses they have to be assigned to your network interface vtnet0:

/etc/rc.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ifconfig_vtnet0_aliases="\
                      inet6 2a03:b0c0:2:d0::2a5:f002 prefixlen 64 \
                      inet6 2a03:b0c0:2:d0::2a5:f003 prefixlen 64 \
                      inet6 2a03:b0c0:2:d0::2a5:f004 prefixlen 64 \
                      inet6 2a03:b0c0:2:d0::2a5:f005 prefixlen 64 \
                      inet6 2a03:b0c0:2:d0::2a5:f006 prefixlen 64 \
                      inet6 2a03:b0c0:2:d0::2a5:f007 prefixlen 64 \
                      inet6 2a03:b0c0:2:d0::2a5:f008 prefixlen 64 \
                      inet6 2a03:b0c0:2:d0::2a5:f009 prefixlen 64 \
                      inet6 2a03:b0c0:2:d0::2a5:f00a prefixlen 64 \
                      inet6 2a03:b0c0:2:d0::2a5:f00b prefixlen 64 \
                      inet6 2a03:b0c0:2:d0::2a5:f00c prefixlen 64 \
                      inet6 2a03:b0c0:2:d0::2a5:f00d prefixlen 64 \
                      inet6 2a03:b0c0:2:d0::2a5:f00e prefixlen 64 \
                      inet6 2a03:b0c0:2:d0::2a5:f00f prefixlen 64"

In case you want to add freebsd jails later on, it is a good idea to allocate private ipv4 addresses for these too. In my case I generated as many ipv4 address as ipv6 addresses I got:

/etc/rc.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cloned_interfaces="${cloned_interfaces} lo1"
ifconfig_lo1_aliases="\
                      inet 192.168.67.1/24 \
                      inet 192.168.67.2/24 \
                      inet 192.168.67.3/24 \
                      inet 192.168.67.4/24 \
                      inet 192.168.67.5/24 \
                      inet 192.168.67.6/24 \
                      inet 192.168.67.7/24 \
                      inet 192.168.67.8/24 \
                      inet 192.168.67.9/24 \
                      inet 192.168.67.10/24 \
                      inet 192.168.67.11/24 \
                      inet 192.168.67.12/24 \
                      inet 192.168.67.13/24 \
                      inet 192.168.67.14/24 \
                      inet 192.168.67.15/24"

To apply these network settings immediately issue the following commands in series:

/etc/rc.conf
1
sudo service netif restart; sudo /etc/rc.d/routing restart

The second command is important because it adds the ipv4 gateway back. Otherwise you will not reach your droplet via ipv4 without rebooting.

If everything still works, you can remove, the following files leftover from cloudflare’s provisioning:

/etc/rc.conf
1
2
3
4
rm /etc/rc.d/digitalocean
rm -r /etc/rc.digitalocean.d
rm -r /usr/local/bsd-cloudinit/
pkg remove avahi-autoipd

Static MAC-Address for Bananapi

The bananapi does currently assign random mac addresses to its ethnernet nic, which is bad if you want to assign static dhcp leases. To solve this issue just create the following udev rule:

/etc/udev/rules.d/75-static-mac
1
ACTION=="add", SUBSYSTEM=="net", ATTR{dev_id}=="0x0", RUN+="/usr/bin/ip link set dev %k address XX:XX:XX:XX:XX:XX"

Replace XX:XX:XX:XX:XX:XX with your current mac address:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ ip address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN group
default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP
group default qlen 1000
    link/ether 02:8a:03:43:02:2a brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.56/24 brd 192.168.1.255 scope global eth0
    inet6 fe80::8a:3ff:fe43:22a/64 scope link
       valid_lft forever preferred_lft forever
    inet6 fe80::9985:bd71:3b59:4875/64 scope link
       valid_lft forever preferred_lft forever

which is 02:8a:03:43:02:2a in my case.

Remove Current binding.pry From Pry

If you are a ruby user and find it annoying to remove binding.pry by hand, you may find the following snippet useful. (Put it in your ~/.pryrc to use it)

.pryrc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Pry.config.commands.command "remove-pry", "Remove current pry" do
  require 'pry/commands/edit/file_and_line_locator'
  file_name, remove_line =
Pry::Command::Edit::FileAndLineLocator.from_binding(_pry_.current_binding)
  temp_file = Tempfile.new('foo')
  i = 0
  File.foreach(file_name) do |line|
    i += 1
    if i == remove_line
      line.gsub!(/binding.pry(\s)?/, "")
      temp_file.write line unless line =~ /\A[[:space:]]*\z/
    else
      temp_file.write line
    end
  end
  temp_file.close
  FileUtils.cp(temp_file.path, file_name)
end

Usage

Before:

debug.rb
1
2
3
4
5
6
# ...
if foo == :bar
  binding.pry
  a_shiny_method
end
# ...
in pry
1
pry> remove-pry

After:

debug.rb
1
2
3
4
5
# ...
if foo == :bar
  a_shiny_method
end
# ...

Ferm Rules for Docker

The Docker daemon add his own custom rules by default to iptables. If you use ferm to manage your iptables rules, it is a good idea to prepopulate rules for docker. Otherwise they will be overwritten by ferm as it restarts.

To do so add the following lines at the top of your ferm.conf:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
domain ip {
    table filter chain FORWARD {
        outerface docker0 mod conntrack ctstate (RELATED ESTABLISHED) ACCEPT;
        interface docker0 outerface !docker0 ACCEPT;
        interface docker0 outerface docker0 ACCEPT;
    }
    table nat {
        chain DOCKER;
        chain PREROUTING {
           mod addrtype dst-type LOCAL jump DOCKER;
        }
        chain OUTPUT {
           daddr !127.0.0.0/8 mod addrtype dst-type LOCAL jump DOCKER;
        }

        chain POSTROUTING {
           saddr 172.17.0.0/16 outerface !docker0 MASQUERADE;
        }
    }
}

In my case docker’s subnet is 172.17.0.0/16 and uses docker0 as bridge device.

Gathering Crash-reports and User-feedback for Your Android App

tl;dr: How-to use ACRA and a PHP-script for getting fairly pretty crash-reports and user-feedback via email (without ugly Android email-Intents)

Introduction

I’m the developer of OpenTraining, an open source Android app for fitness training. I recently looked for a possibility to add a simple feedback system to my app. There’s an open source framework for crash reports named ACRA that I decided to use for both crash reports and user feedback.

The Google Play Store offers a crash report system as well, but if you deploy your app on multiple app stores you might want a central instance for collecting crash reports. For user feedback many apps simply open an email-Intent but I don’t think this offers a good user experience.

This is how the user feedback dialog and the generated mail look like:

Android feedback dialog and feedback mail

Advantages:

  • simple
  • self-hosted
  • good workflow for smaller projects
  • only PHP required

Disadvantages:

  • does not scale (e.g. if you have 50.000+ users)

If your project is pretty large you should consider another ACRA-backend. I tried some of them, but as long as I get < 20 emails per week I’ll use the PHP backend.

This How-to is based on ACRA and ACRA-mailer.

How-To

The most important changes I had to apply to my project for adding the feedback-feature can be seen in this commit on GitHub (but there have been some more commits concerning ACRA).

1. Add ACRA to your project

  • Open your Eclipse project
  • Add the file acra-4.X.Y.jar to the libs folder
  • Right-click on the jar file -> add to build path

If you have any problems with this step have a look at the ACRA documentation. There’s also a description for Gradle integration.

2. Use the ACRA library

Create a new class that extends Application:

import org.acra.*;
import org.acra.annotation.*;

import android.app.Application;


@ReportsCrashes(
    formKey = "" // This is required for backward compatibility but not used
 )


public class YourApplication extends Application{

    @Override
     public void onCreate() {
         super.onCreate();

         // The following line triggers the initialization of ACRA
         ACRA.init(this);
         ACRA.getErrorReporter().setReportSender(new ACRACrashReportMailer()); // default crash report sender
    }


}

Open the android manifest editor (AndroidManifest.xml)

  • In the Application tab, click on the Browse button next to the Name field
  • Select your newly created Application class

Make sure that your application requests the permission ‘android.permission.INTERNET’.

3. Add ReportSender

I use 2 different implementations of ReportSender:

The crash reporter sends nearly all data that’s available, the feedback reporter sends the user message, the date and the app version. Add both to your project.

Remember to change the ‘BASE_URL’. Use HTTPS if your server supports it (mine doesn’t).

4. Add PHP scripts

There are 2 PHP scripts as well:

You will also need the mail template. Change the destination email and add the files to the webspace/server of your choice (e.g. uberspace). If you want you can change the “shared_secret”, but remember to do this in the Java class as well.

5. Test receiving feedback

Now you should have a try and test sending feedback to yourself:

ACRA.getErrorReporter().setReportSender(new ACRAFeedbackMailer());
ACRA.getErrorReporter().putCustomData("User message", "Some Text here");
ACRA.getErrorReporter().handleSilentException(new NullPointerException("Test"));

If this works you need a suitable spot for your user feedback. In most cases a dialog should be fine.

Consider to write your own class(es) that extend(s) Exception. Your PHP script could do further processing with this information.

Ideas for further improvements

Improve the email formatting

As you have a server-side script it is very easy to change the formatting of the emails. Highlighting the user comments or the type of exception may be a good first step.

Usage for larger projects

With the use of two different implementations of ReportSender it is also possible to use email only for sending feedback and send crash reports to another backend that is better suited for bug tracking. For larger projects this approach is recommended.

by Christian Skubich

eMail: christian@skubware.de

Twitter: @chaosbastler

Internet Sharing (Ipv4 and Ipv6) on Archlinux Using Dnsmasq

Update: Added adhoc wlan network

A guide to connect with a different machine using a ethernet cable for internet sharing or just transferring files:

  1. Install dnsmasq and iproute2

    $ pacman -S dnsmasq iproute2

  2. Copy over the configuration files at the end of the article and edit the /etc/conf.d/share-internet@\<device> to match your network setup. (where \<device> is your network device)

  3. Start the sharing service with systemd

    $ sudo systemctl start internet-sharing@.service

After that the other machine can connect via dhcp. It will get an ipv4 address from the 10.20.0.0/24 subnet and a ipv6 address from the fd21:30c2:dd2f:: subnet. Your host will be reachable via 10.20.0.1 or fd21:30c2:dd2f::1. Thanks to ipv6 router advertising, an AAAA record for each host is automatically set based on the hostname. This means if your hostname is foo, all members of the network can just connect to it using the address foo. You should disable the share-internet.service, if you don’t need it. Otherwise you might mess up network setups, if you connect to a network with the device on which the dhcp service is running.

Happy networking!

File /var/lib/gitlab/builds/blog.higgsboson.tk/source/downloads/code/share-internet/share-internet.service could not be found

/etc/dnsmasq.conf (dnsmasq.conf) download
1
2
3
4
5
# google as an upstream dns server
server=8.8.8.8
server=8.8.4.4
no-resolv
cache-size=2000

Ethernet to Wlan:

/etc/conf.d/share-internet@enp0s25 (share-internet@enp0s25) download
1
2
3
4
5
6
7
8
9
10
# Device which has internet access, ex: wlan0 or usb0
EXTERNAL_DEVICE="wlp3s0"

IP4_ADDRESS="10.20.0.1"
IP4_NETMASK="24"
IP4_SUBNET="10.20.0.2,10.20.0.255"

IP6_ADDRESS="fd21:30c2:dd2f::1"
IP6_NETMASK="64"
IP6_SUBNET="fd21:30c2:dd2f::"

Wlan to Ethernet:

If you have luck and your wifi driver is capable of the infrastructure mode, you should take a look at hostadp, in my case I have to create an adhoc network. To enable the adhoc network:

$ sudo systemctl enable wireless-adhoc@\<device>.service

/etc/conf.d/share-internet@wlp3s0 (share-internet@wlp3s0) download
1
2
3
4
5
6
7
8
9
10
# Device which has internet access, ex: wlan0 or usb0
EXTERNAL_DEVICE="enp0s20u2"

IP4_ADDRESS="10.20.0.1"
IP4_NETMASK="24"
IP4_SUBNET="10.20.0.100,10.20.0.199"

IP6_ADDRESS="fd21:30c2:dd2f::1"
IP6_NETMASK="64"
IP6_SUBNET="fd21:30c2:dd2f::"
/etc/systemd/system/wireless-adhoc@.service (wireless-adhoc@.service) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Unit]
Description=Ad-hoc wireless network connectivity (%i)
Wants=network.target
Before=network.target
Conflicts=netctl-auto@.service
BindsTo=sys-subsystem-net-devices-%i.device
After=sys-subsystem-net-devices-%i.device

[Service]
Type=simple
ExecStartPre=/usr/bin/rfkill unblock wifi
ExecStart=/usr/sbin//wpa_supplicant -D nl80211,wext -c/etc/wpa_supplicant/wpa_supplicant-adhoc-%I.conf -i%I

[Install]
RequiredBy=share-internet@%i.service
/etc/wpa_supplicant/wpa_supplicant-adhoc-wlp3s0.conf (wpa_supplicant-adhoc-wlp3s0.conf) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
ctrl_interface=DIR=/run/wpa_supplicant GROUP=wheel

# use 'ap_scan=2' on all devices connected to the network
ap_scan=2

network={
    ssid="The.Secure.Network"
    mode=1
    frequency=2432
    proto=WPA
    key_mgmt=WPA-NONE
    pairwise=NONE
    group=TKIP
    psk="fnord"
}

# MacOS X and Networmanager aren't capable of using WPA/WPA2 for Adhoc Networks
#network={
#    ssid="The.Insecure.Network"
#    mode=1
#    frequency=2432
#    proto=WPA
#    key_mgmt=NONE
#    pairwise=NONE
#    group=TKIP
#
#    wep_key0="fnord"
#    wep_tx_keyidx=0
#}

Busybox to the Rescue

Some days before I broke my raspberry pie, after pacman running out of memory, while updating my glibc. To solve such problems on any of my machines, I decided to setup rescue systems with busybox. Therefor just install the package busybox on archlinux or busybox-static if you are on debian. Busybox is a so called multi-call binary. This means, it exposes different behaviour depending on the program name, which is used to execute it. As a basic environment for the rescue system, I created a symlinks for every command which busybox is capable of:

$ sudo mkdir /opt/busybox/bin

$ busybox --list | xargs -n 1 -d "\n" -I "cmd" sudo ln -s $(which busybox) /opt/busybox/bin/cmd

In order to be able to login in a system, where the usual shell is broken, I added a new user called rescue.

$ useradd -m -s /opt/busybox/bin/ash rescue

Because origin passwd uses sha256 for password hashes, which busybox is not capable of by default you have to recreate every password, you plan to login, to make things like su work:

$ sudo busybox passwd -a 2 rescue # use sha1 instead of sha256
$ sudo busybox passwd -a 2 root

The login shell is set in this case to the one busybox provides. In order to be able to login via ssh this shell has to be added /etc/shells:

$ echo /opt/busybox/bin/ash | sudo tee -a /etc/shells

The last thing left, is to prepend the path with busybox symlinks, to the PATH variable of the rescue user, to use them instead of their coreutils equivalents.

$ echo 'export PATH=/opt/busybox/bin:$PATH' | sudo tee -a /home/rescue/.profile