Creating Android apps natively on your phone

Creating Android apps natively on your phone

You can build APKs directly in a Debian chroot on your ARM-based phone. I have successfully done this using qemu-i386-static on my phone.

English translation: You can have a fully portable development environment right on your phone, using the Android SDK.

Prerequisites:

  1. You have a Debian chroot installation on your phone.
    1. Here is one way to get one.

  2. You have an X desktop within your chroot and can VNC/XRDP into it.
  3. You know what Debian’s “debootstrap” is.

Steps:

  1. Install a debian chroot environment on your phone.
  2. Make sure you have qemu-user-static, openjdk-6, ant, and libswt-gtk-3-java installed
    1. The section “My Debian chroot environment” below has my working setup, in case I am leaving out any dependencies.
  3. Install an i386 chroot within your Debian chroot.
    1. debootstrap –arch=i386 –variant=minbase –include=zlib1g –foreign sid .
    2. With the “normal qemu-i386-static” you won’t be able to chroot into this environment to run debootstrap –second-stage. It seems to work fine without running the second stage, but if you want to do that, then see the notes below.
  4. Download the Android SDK for Linux i386 and extract it.
  5. Run export ANDROID_SWT=/usr/share/java/swt.jar

    1. I added this line to the beginning of the android-sdk-linux/tools/android script.
  6. cd to your android-sdk-linux/tools and run ./android
  7. Install at least one API.
  8. Install the two scripts below run-i386 and run-i386-link into a bin folder within your path.
    1. I created a bin folder under my username, and added it to my path in my .bashrc file.
  9. In android-sdk-linux/tools, run run-i386 mklinks

    1. This runs the supplied Android tools in the Qemu emulator each time one is called.
  10. In android-sdk-linux/platform-tools run run-i386 mklinks

  11. If android-sdk-linux/tools is in your PATH, you should now be able to run “android create project” to create a new Android project:
    1. android create project -n hello -t android-10 -p pwd -k com.fieldeffect.hello -a Hello

  12. Now you can:
    1. cd <project folder>

    2. ant debug
    3. And, if all goes well, you should have a hello-debug.apk in your folder.
    4. Copy it into your Android environment and install.

Enjoy!

Scripts:

run-i386 (Change the QEMU and I386CHROOT parameters to your own directories):

#!/bin/bash

QEMU=/usr/bin/qemu-i386-static
I386CHROOT=~/Dev/chroot-i386

case "$1" in

mklinks)
        if [ ! -e "./I386" ]; then
                mkdir ./I386
        fi
        
        for i in $(file ./* | grep "ELF 32" | awk '{print $1}' | sed s/://g | sed s/[./]//g); do
                echo "Moving $i..."
                mv $i ./I386
                ln -s ~/bin/run-i386-link $i
        done
        
;;

*)
$QEMU $I386CHROOT/lib/ld-linux.so.2 --library-path $I386CHROOT/lib:$I386CHROOT/usr/lib:$I386CHROOT/usr/share/perl/5.12.4/unicore/lib:$I386CHROOT/var/lib:$I386CHROOT/lib/i386-linux-gnu:$I386CHROOT/usr/lib/i386-linux-gnu [email protected]
;;

esac

run-i386-link:

echo $(dirname $0)/I386/$(basename $0) [email protected]

~/bin/run-i386 $(dirname $0)/I386/$(basename $0) [email protected]

Appendix A: My Debian chroot environment

To run exactly the environment I currently have on my Android phone, on my 4GB sdcard.

cd <working-directory>

debootstrap --arch=armel --variant=minbase --foreign --include adduser,ant,ant-optional,apt,apt-utils,apt-xapian-index,aptitude,aptitude-doc-en,aspell,aspell-en,base-files,base-passwd,bash,binfmt-support,binutils,bsdmainutils,bsdutils,build-essential,bzip2,ca-certificates,ca-certificates-java,coreutils,cpp,cpp-4.6,dash,dbus,dbus-x11,dconf-gsettings-backend,debconf,debconf-i18n,debian-archive-keyring,debianutils,debootstrap,defoma,dialog,dictionaries-common,diffutils,dillo,dos2unix,dpkg,dpkg-dev,dropbear,dselect,e2fslibs,e2fsprogs,esound-common,fakeroot,file,findutils,fontconfig,fontconfig-config,g++,g++-4.6,gcc,gcc-4.4-base,gcc-4.5-base,gcc-4.6,gcc-4.6-base,gconf2,gconf2-common,geany,glib-networking,gnome-icon-theme,gnupg,gpgv,grep,groff-base,gsettings-desktop-schemas,gzip,hdparm,hicolor-icon-theme,hostname,hunspell-en-us,icedtea-6-jre-cacao,icedtea-6-jre-jamvm,icedtea-netx,icedtea-netx-common,icewm,icewm-common,icewm-themes,ifupdown,initscripts,insserv,iso-codes,java-common,keyboard-configuration,less,libaccess-bridge-java,libaccess-bridge-java-jni,libacl1,libalgorithm-diff-perl,libalgorithm-diff-xs-perl,libalgorithm-merge-perl,libasound2,libaspell15,libasyncns0,libatk1.0-0,libatk1.0-data,libattr1,libaudiofile0,libaudit0,libauthen-ntlm-perl,libavahi-client3,libavahi-common-data,libavahi-common3,libblkid1,libboost-iostreams1.46.1,libbsd0,libbz2-1.0,libc-bin,libc-dev-bin,libc6,libc6-dev,libcairo-gobject2,libcairo2,libcanberra-gtk3-0,libcanberra-gtk3-module,libcanberra0,libcap2,libclass-accessor-perl,libcomerr2,libcroco3,libcups2,libcwidget3,libdatrie1,libdb4.8,libdb5.1,libdbus-1-3,libdbus-glib-1-2,libdigest-hmac-perl,libdpkg-perl,libdrm-radeon1,libdrm2,libedit2,libenchant1c2a,libencode-locale-perl,libept1,libesd0,libexpat1,libfile-listing-perl,libflac8,libfltk1.3,libfont-afm-perl,libfont-freetype-perl,libfontconfig1,libfontenc1,libfreetype6,libgail18,libgc1c2,libgcc1,libgconf2-4,libgcrypt11,libgdbm3,libgdk-pixbuf2.0-0,libgeoclue0,libgif4,libgl1-mesa-dri,libgl1-mesa-glx,libglapi-mesa,libglib2.0-0,libglib2.0-data,libgmp10,libgnutls26,libgomp1,libgpg-error0,libgpm2,libgssapi-krb5-2,libgstreamer-plugins-base0.10-0,libgstreamer0.10-0,libgtk-3-0,libgtk-3-bin,libgtk-3-common,libgtk2.0-0,libgtk2.0-bin,libgtk2.0-common,libhtml-form-perl,libhtml-format-perl,libhtml-parser-perl,libhtml-tagset-perl,libhtml-template-perl,libhtml-tree-perl,libhttp-cookies-perl,libhttp-daemon-perl,libhttp-date-perl,libhttp-message-perl,libhttp-negotiate-perl,libhunspell-1.3-0,libice-dev,libice6,libicu44,libidl0,libidn11,libio-socket-inet6-perl,libio-socket-ssl-perl,libio-string-perl,libjasper1,libjpeg62,libjpeg8,libjson0,libk5crypto3,libkeyutils1,libkrb5-3,libkrb5support0,liblcms1,libldap-2.4-2,liblocale-gettext-perl,libltdl7,liblwp-mediatypes-perl,liblwp-protocol-https-perl,liblzma2,liblzma5,libmagic1,libmailtools-perl,libmount1,libmpc2,libmpfr4,libncurses5,libncursesw5,libnet-http-perl,libnet-ssleay-perl,libnotify4,libnspr4-0d,libnss3-1d,libogg0,liborbit2,libp11-kit0,libpam-modules,libpam-modules-bin,libpam-runtime,libpam0g,libpango1.0-0,libparse-debianchangelog-perl,libpci3,libpciaccess0,libpcre3,libpipeline1,libpixman-1-0,libpng12-0,libproxy0,libpthread-stubs0,libpthread-stubs0-dev,libpulse0,libreadline5,libreadline6,librsvg2-2,librsvg2-common,libsamplerate0,libsasl2-2,libsasl2-modules,libselinux1,libsepol1,libsigc++-2.0-0c2a,libslang2,libsm-dev,libsm6,libsndfile1,libsocket6-perl,libsoup2.4-1,libsqlite3-0,libss2,libssl1.0.0,libstdc++6,libstdc++6-4.6-dev,libsub-name-perl,libswt-gtk-3-java,libswt-gtk-3-jni,libtasn1-3,libtdb1,libtext-charwidth-perl,libtext-iconv-perl,libtext-wrapi18n-perl,libthai-data,libthai0,libtiff4,libtimedate-perl,libtinfo5,libudev0,libunique-1.0-0,liburi-perl,libusb-0.1-4,libusb-1.0-0,libutempter0,libuuid1,libvorbis0a,libvorbisenc2,libvorbisfile3,libvte-common,libvte9,libwebkitgtk-1.0-0,libwebkitgtk-1.0-common,libwrap0,libwww-perl,libwww-robotrules-perl,libx11-6,libx11-data,libx11-dev,libx11-xcb1,libxapian22,libxau-dev,libxau6,libxaw7,libxcb-render0,libxcb-shape0,libxcb-shm0,libxcb1,libxcb1-dev,libxcomposite1,libxcursor1,libxdamage1,libxdmcp-dev,libxdmcp6,libxerces2-java,libxext6,libxfixes3,libxfont1,libxft2,libxi6,libxinerama1,libxkbfile1,libxml-commons-external-java,libxml-commons-resolver1.1-java,libxml-namespacesupport-perl,libxml-parser-perl,libxml-sax-base-perl,libxml-sax-expat-perl,libxml-sax-perl,libxml-simple-perl,libxml2,libxmu6,libxmuu1,libxpm4,libxrandr2,libxrender1,libxslt1.1,libxss1,libxt-dev,libxt6,libxtst6,libxv1,libxxf86dga1,libxxf86vm1,linux-libc-dev,login,lsb-base,lsb-release,lsof,lxterminal,make,man-db,manpages,manpages-dev,mawk,menu,midori,mime-support,mount,multiarch-support,ncurses-base,ncurses-bin,net-tools,netbase,netcat,netcat-traditional,notification-daemon,openjdk-6-jdk,openjdk-6-jre,openjdk-6-jre-headless,openjdk-6-jre-lib,openssh-blacklist,openssh-blacklist-extra,openssh-client,openssl,passwd,patch,pciutils,perl,perl-base,perl-modules,procps,psmisc,python,python-apt,python-apt-common,python-chardet,python-debian,python-minimal,python-support,python-xapian,python2.6,python2.6-minimal,python2.7,python2.7-minimal,qemu-user-static,readline-common,screen,sed,sensible-utils,sgml-base,shared-mime-info,socat,sqlite3,sysv-rc,sysvinit,sysvinit-utils,tar,tcpd,tightvncserver,ttf-bitstream-vera,ttf-dejavu-core,ttf-dejavu-extra,tzdata,tzdata-java,ucf,udev,usbutils,util-linux,vim-common,vim-tiny,w3m,wget,x-ttcidfont-conf,x11-common,x11-utils,x11-xkb-utils,x11-xserver-utils,x11proto-core-dev,x11proto-input-dev,x11proto-kb-dev,xauth,xbitmaps,xfonts-100dpi,xfonts-75dpi,xfonts-base,xfonts-encodings,xfonts-scalable,xfonts-utils,xkb-data,xml-core,xorg-sgml-doctools,xrdp,xserver-common,xserver-xorg-core,xserver-xorg-video-dummy,xterm,xtrans-dev,xz-utils,zlib1g,zlib1g-dev sid .

APPENDIX B: Notes

Notes on QEMU i386 chroot environment:

You currently can’t do an i386 chroot on an ARM device with qemu-i386-static. A user has submitted a patch to allow you to do this, but you must compile QEMU from source, and it only works with qemu-0.14.1. The above process to run the SDK is confirmed to work without running a –second-stage on the debootstrap; however, if you do want to complete the i386 chroot installation you will currently have to use the below.

Furthermore, I would only use this particular binary for the chroot, and not for the SDK process above — for that I would use qemu-user-static 0.15+. I got a Segmentation Fault when trying to use it for the Android SDK tools.

apt-get install build-essential

wget http://download.savannah.gnu.org/releases/qemu/qemu-0.14.1.tar.gz

#Copy and paste patch from here: http://patchwork.ozlabs.org/patch/45206/
# ... into patch.txt

#Untar and apply patch to qemu source code
tar xzf qemu-0.14.1.tar.gz
cd qemu-0.14.1
patch -p1 < ../patch.txt

#Native compiling
./configure --target-list="i386-softmmu i386-linux-user" --static
make -j2

Ozwork labs patch:

diff --git a/configure b/configure
index 0a84b0e..69ccb13 100755
--- a/configure
+++ b/configure
@@ -2342,6 +2342,7 @@  TARGET_ABI_DIR=""
 case "$target_arch2" in
   i386)
     target_phys_bits=32
+    target_nptl="yes"
   ;;
   x86_64)
     TARGET_BASE_ARCH=i386
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 9fb493f..38f2067 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -3426,6 +3426,13 @@  static abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr)
     unlock_user_struct(target_ldt_info, ptr, 1);
     return 0;
 }
+
+static inline void cpu_set_tls(CPUState *env, target_ulong newtls)
+{
+    do_set_thread_area(env, newtls);
+    /* reload gs */
+    cpu_x86_load_seg(env, R_GS, env->segs[R_GS].selector);
+}
 #endif /* TARGET_I386 && TARGET_ABI32 */
 
 #ifndef TARGET_ABI32
@@ -3554,7 +3561,16 @@  static int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp,
         new_stack = ts->stack;
         /* we create a new CPU instance. */
         new_env = cpu_copy(env);
-#if defined(TARGET_I386) || defined(TARGET_SPARC) || defined(TARGET_PPC)
+#if defined(TARGET_I386)
+        new_env->idt.base = target_mmap(0, sizeof(uint64_t) * (env->idt.limit + 1),
+                PROT_READ|PROT_WRITE,
+                MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+        memcpy(g2h(new_env->idt.base), g2h(env->idt.base), sizeof(uint64_t) * (env->idt.limit + 1));
+        new_env->gdt.base = target_mmap(0, sizeof(uint64_t) * TARGET_GDT_ENTRIES,
+                PROT_READ|PROT_WRITE,
+                MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+        memcpy(g2h(new_env->gdt.base), g2h(env->gdt.base), sizeof(uint64_t) * TARGET_GDT_ENTRIES);
+#elif defined(TARGET_SPARC) || defined(TARGET_PPC)
         cpu_reset(new_env);
 #endif
         /* Init regs that differ from the parent.  */