diff --git a/android/README.md b/android/README.md new file mode 100644 index 00000000..b05c260a --- /dev/null +++ b/android/README.md @@ -0,0 +1,53 @@ +Copyright (c) 2014-2017, The Monero Project + + +## Current status : ALPHA + + - Minimum Android 5.0 (api level 21) + - Modal dialogs can appear in background giving the feeling that the application is frozen (Work around : turn screen off/on or switch to another app and back) + +## Build using Docker + +# Base environnement + + cd monero/utils/build_scripts + docker build -f android32.Dockerfile -t monero-android . + cd .. + +# Build GUI + + cd android/docker + docker build -t monero-gui-android . + docker create -it --name monero-gui-android monero-gui-android bash + +# Get the apk + + docker cp monero-gui-android:/opt/android/monero-core/build/release/bin/bin/QtApp-debug.apk . + +## Deployment + +- Using ADB (Android debugger bridge) : + + First, see section [Enable adb debugging on your device](https://developer.android.com/studio/command-line/adb.html#Enabling) + The only place where we are allowed to play is `/data/local/tmp`. So : + + adb push /opt/android/monero-core/build/release/bin/bin/QtApp-debug.apk /data/local/tmp + adb shell pm install -r /data/local/tmp/QtApp-debug.apk + + - Troubleshooting: + + adb devices -l + adb logcat + + if using adb inside docker, make sure you did "docker run -v /dev/bus/usb:/dev/bus/usb --privileged" + +- Using a web server + + mkdir /usr/tmp + cp QtApp-debug.apk /usr/tmp + docker run -d -v /usr/tmp:/usr/share/nginx/html:ro -p 8080:80 nginx + + Now it should be accessible through a web browser at + + http://:8080/QtApp-debug.apk + diff --git a/android/docker/Dockerfile b/android/docker/Dockerfile new file mode 100644 index 00000000..5f1b1e53 --- /dev/null +++ b/android/docker/Dockerfile @@ -0,0 +1,108 @@ +FROM monero-android + +#INSTALL JAVA +RUN echo "deb http://ftp.fr.debian.org/debian/ jessie-backports main contrib non-free" >> /etc/apt/sources.list +RUN dpkg --add-architecture i386 \ + && apt-get update \ + && apt-get install -y libc6:i386 libncurses5:i386 libstdc++6:i386 libz1:i386 \ + && apt-get install -y -t jessie-backports ca-certificates-java openjdk-8-jdk-headless openjdk-8-jre-headless ant +ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64 +ENV PATH $JAVA_HOME/bin:$PATH + +#Get Qt +ENV QT_VERSION 5.8 + +RUN git clone git://code.qt.io/qt/qt5.git -b ${QT_VERSION} \ + && cd qt5 \ + && perl init-repository + +## Note: Need to use libc++ but Qt does not provide mkspec for libc++. +## Their support of it is quite recent and they claim they don't use it by default +## [only because it produces bigger binary objects](https://bugreports.qt.io/browse/QTBUG-50724). + +#Create new mkspec for clang + libc++ +RUN cp -r qt5/qtbase/mkspecs/android-clang qt5/qtbase/mkspecs/android-clang-libc \ + && cd qt5/qtbase/mkspecs/android-clang-libc \ + && sed -i '16i ANDROID_SOURCES_CXX_STL_LIBDIR = $$NDK_ROOT/sources/cxx-stl/llvm-libc++/libs/$$ANDROID_TARGET_ARCH' qmake.conf \ + && sed -i '17i ANDROID_SOURCES_CXX_STL_INCDIR = $$NDK_ROOT/sources/cxx-stl/llvm-libc++/include' qmake.conf \ + && echo "QMAKE_LIBS_PRIVATE = -lc++_shared -llog -lz -lm -ldl -lc -lgcc " >> qmake.conf \ + && echo "QMAKE_CFLAGS -= -mfpu=vfp " >> qmake.conf \ + && echo "QMAKE_CXXFLAGS -= -mfpu=vfp " >> qmake.conf \ + && echo "QMAKE_CFLAGS += -mfpu=vfp4 " >> qmake.conf \ + && echo "QMAKE_CXXFLAGS += -mfpu=vfp4 " >> qmake.conf + +ENV ANDROID_API android-21 + +#ANDROID SDK TOOLS +RUN echo y | $ANDROID_SDK_ROOT/tools/android update sdk --no-ui --all --filter platform-tools +RUN echo y | $ANDROID_SDK_ROOT/tools/android update sdk --no-ui --all --filter ${ANDROID_API} +RUN echo y | $ANDROID_SDK_ROOT/tools/android update sdk --no-ui --all --filter build-tools-25.0.1 + +ENV CLEAN_PATH $JAVA_HOME/bin:/usr/cmake-3.6.3-Linux-x86_64/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +#build Qt +RUN cd qt5 && PATH=${CLEAN_PATH} ./configure -developer-build -release \ + -xplatform android-clang-libc \ + -android-ndk-platform ${ANDROID_API} \ + -android-ndk $ANDROID_NDK_ROOT \ + -android-sdk $ANDROID_SDK_ROOT \ + -opensource -confirm-license \ + -prefix ${WORKDIR}/Qt-${QT_VERSION} \ + -nomake tests -nomake examples \ + -skip qtserialport \ + -skip qtconnectivity \ + -skip qttranslations \ + -skip qtgamepad -skip qtscript -skip qtdoc + +# build Qt tools : gnustl_shared.so is hard-coded in androiddeployqt +# replace it with libc++_shared.so +COPY androiddeployqt.patch qt5/qttools/androiddeployqt.patch +RUN cd qt5/qttools \ + && git apply androiddeployqt.patch \ + && cd .. \ + && PATH=${CLEAN_PATH} make -j4 \ + && PATH=${CLEAN_PATH} make install + +# Get iconv and ZBar +ENV ICONV_VERSION 1.14 +RUN git clone https://github.com/ZBar/ZBar.git \ + && curl -s -O http://ftp.gnu.org/pub/gnu/libiconv/libiconv-${ICONV_VERSION}.tar.gz \ + && tar -xzf libiconv-${ICONV_VERSION}.tar.gz \ + && cd libiconv-${ICONV_VERSION} \ + && CC=arm-linux-androideabi-clang CXX=arm-linux-androideabi-clang++ ./configure --build=x86_64-linux-gnu --host=arm-eabi --prefix=${WORKDIR}/libiconv --disable-rpath + +ENV PATH $ANDROID_SDK_ROOT/tools:$ANDROID_SDK_ROOT/platform-tools:${WORKDIR}/Qt-${QT_VERSION}/bin:$PATH + +#Build libiconv.a and libzbarjni.a +COPY android.mk.patch ZBar/android.mk.patch +RUN cd ZBar \ + && git apply android.mk.patch \ + && echo \ +"APP_ABI := armeabi-v7a \n\ +APP_STL := c++_shared \n\ +TARGET_PLATFORM := ${ANDROID_API} \n\ +TARGET_ARCH_ABI := armeabi-v7a \n\ +APP_CFLAGS += -target armv7-none-linux-androideabi -fexceptions -fstack-protector-strong -fno-limit-debug-info -mfloat-abi=softfp -mfpu=vfp -fno-builtin-memmove -fno-omit-frame-pointer -fno-stack-protector\n"\ + >> android/jni/Application.mk \ + && cd android \ + && android update project --path . -t "${ANDROID_API}" \ + && CC=arm-linux-androideabi-clang CXX=arm-linux-androideabi-clang++ ant -Dndk.dir=${ANDROID_NDK_ROOT} -Diconv.src=${WORKDIR}/libiconv-${ICONV_VERSION} zbar-clean zbar-ndk-build + +#Can't directly call build.sh because of env variables +RUN git clone https://github.com/monero-project/monero-core.git \ + && cd monero-core \ + && git submodule update \ + && CC=arm-linux-androideabi-clang CXX=arm-linux-androideabi-clang++ BOOST_ROOT=/opt/android/boost_1_62_0 BOOST_LIBRARYDIR=${WORKDIR}/boost_${BOOST_VERSION}/android32/lib/ OPENSSL_ROOT_DIR=${WORKDIR}/openssl/ ./get_libwallet_api.sh release-android + +RUN cp openssl/lib* ${ANDROID_NDK_ROOT}/platforms/${ANDROID_API}/arch-arm/usr/lib +RUN cp boost_${BOOST_VERSION}/android32/lib/lib* ${ANDROID_NDK_ROOT}/platforms/${ANDROID_API}/arch-arm/usr/lib +RUN cp ZBar/android/obj/local/armeabi-v7a/lib* ${ANDROID_NDK_ROOT}/platforms/${ANDROID_API}/arch-arm/usr/lib + +ENV PATH $ANDROID_SDK_ROOT/tools:$ANDROID_SDK_ROOT/platform-tools:${WORKDIR}/Qt-${QT_VERSION}/bin:$CLEAN_PATH + +# NB : zxcvbn-c needs to build a local binary and Qt don't care about these environnement variable +RUN cd monero-core \ + && CC="gcc" CXX="g++" ./build.sh release-android \ + && cd build \ + && make deploy + diff --git a/android/docker/android.mk.patch b/android/docker/android.mk.patch new file mode 100644 index 00000000..9fd7f172 --- /dev/null +++ b/android/docker/android.mk.patch @@ -0,0 +1,61 @@ +diff --git a/android/jni/Android.mk b/android/jni/Android.mk +index e442b07..158afd5 100644 +--- a/android/jni/Android.mk ++++ b/android/jni/Android.mk +@@ -12,14 +12,18 @@ LOCAL_PATH := $(ICONV_SRC) + + LOCAL_MODULE := libiconv + ++LOCAL_ARM_MODE := arm ++LOCAL_CPP_FEATURES := exceptions rtti features + LOCAL_CFLAGS := \ + -Wno-multichar \ + -D_ANDROID \ +- -DLIBDIR="c" \ ++ -DLIBDIR="\".\"" \ + -DBUILDING_LIBICONV \ + -DBUILDING_LIBCHARSET \ + -DIN_LIBRARY + ++LOCAL_CFLAGS += -fno-stack-protector ++ + LOCAL_SRC_FILES := \ + lib/iconv.c \ + libcharset/lib/localcharset.c \ +@@ -30,13 +34,14 @@ LOCAL_C_INCLUDES := \ + $(ICONV_SRC)/libcharset \ + $(ICONV_SRC)/libcharset/include + +-include $(BUILD_SHARED_LIBRARY) ++include $(BUILD_STATIC_LIBRARY) + + LOCAL_LDLIBS := -llog -lcharset + + # libzbarjni + include $(CLEAR_VARS) + ++ + LOCAL_PATH := $(MY_LOCAL_PATH) + LOCAL_MODULE := zbarjni + LOCAL_SRC_FILES := ../../java/zbarjni.c \ +@@ -71,6 +76,17 @@ LOCAL_C_INCLUDES := ../include \ + ../zbar \ + $(ICONV_SRC)/include + +-LOCAL_SHARED_LIBRARIES := libiconv ++LOCAL_STATIC_LIBRARIES := libiconv ++LOCAL_ARM_MODE := arm ++LOCAL_CPP_FEATURES := exceptions rtti features ++ ++LOCAL_CFLAGS := \ ++ -Wno-multichar \ ++ -D_ANDROID \ ++ -DLIBDIR="\".\"" \ ++ -DBUILDING_LIBICONV \ ++ -DBUILDING_LIBCHARSET \ ++ -DIN_LIBRARY ++ + +-include $(BUILD_SHARED_LIBRARY) +\ No newline at end of file ++include $(BUILD_STATIC_LIBRARY) diff --git a/android/docker/androiddeployqt.patch b/android/docker/androiddeployqt.patch new file mode 100644 index 00000000..b0657677 --- /dev/null +++ b/android/docker/androiddeployqt.patch @@ -0,0 +1,62 @@ +diff --git a/src/androiddeployqt/main.cpp b/src/androiddeployqt/main.cpp +index 8a8e591..71d693e 100644 +--- a/src/androiddeployqt/main.cpp ++++ b/src/androiddeployqt/main.cpp +@@ -1122,7 +1122,7 @@ bool updateLibsXml(const Options &options) + + QString libsPath = QLatin1String("libs/") + options.architecture + QLatin1Char('/'); + +- QString qtLibs = QLatin1String("gnustl_shared\n"); ++ QString qtLibs = QLatin1String("c++_shared\n"); + QString bundledInLibs; + QString bundledInAssets; + foreach (Options::BundledFile bundledFile, options.bundledFiles) { +@@ -2519,6 +2519,39 @@ bool installApk(const Options &options) + return true; + } + ++bool copyStl(Options *options) ++{ ++ if (options->deploymentMechanism == Options::Debug && !options->installApk) ++ return true; ++ ++ if (options->verbose) ++ fprintf(stdout, "Copying LIBC++ STL library\n"); ++ ++ QString filePath = options->ndkPath ++ + QLatin1String("/sources/cxx-stl/llvm-libc++") ++ + QLatin1String("/libs/") ++ + options->architecture ++ + QLatin1String("/libc++_shared.so"); ++ if (!QFile::exists(filePath)) { ++ fprintf(stderr, "LIBC STL library does not exist at %s\n", qPrintable(filePath)); ++ return false; ++ } ++ ++ QString destinationDirectory = ++ options->deploymentMechanism == Options::Debug ++ ? options->temporaryDirectoryName + QLatin1String("/lib") ++ : options->outputDirectory + QLatin1String("/libs/") + options->architecture; ++ ++ if (!copyFileIfNewer(filePath, destinationDirectory ++ + QLatin1String("/libc++_shared.so"), options->verbose)) { ++ return false; ++ } ++ ++ if (options->deploymentMechanism == Options::Debug && !deployToLocalTmp(options, QLatin1String("/lib/libc++_shared.so"))) ++ return false; ++ ++ return true; ++} + bool copyGnuStl(Options *options) + { + if (options->deploymentMechanism == Options::Debug && !options->installApk) +@@ -2870,7 +2903,7 @@ int main(int argc, char *argv[]) + if (Q_UNLIKELY(options.timing)) + fprintf(stdout, "[TIMING] %d ms: Read dependencies\n", options.timer.elapsed()); + +- if (options.deploymentMechanism != Options::Ministro && !copyGnuStl(&options)) ++ if (options.deploymentMechanism != Options::Ministro && !copyStl(&options)) + return CannotCopyGnuStl; + + if (Q_UNLIKELY(options.timing))