diff --git a/.gitignore b/.gitignore index 89a17a3c..b6cffd15 100644 --- a/.gitignore +++ b/.gitignore @@ -237,3 +237,4 @@ pip-log.txt # Sphinx docs/_build +/androidIdea/ diff --git a/Daemon.h b/Daemon.h index fa4f47ec..85b31240 100644 --- a/Daemon.h +++ b/Daemon.h @@ -45,6 +45,20 @@ namespace i2p } }; +#elif defined(ANDROID) +#define Daemon i2p::util::DaemonAndroid::Instance() + // dummy, invoked from android/jni/DaemonAndroid.* + class DaemonAndroid: public i2p::util::Daemon_Singleton + { + public: + + static DaemonAndroid& Instance() + { + static DaemonAndroid instance; + return instance; + } + }; + #elif defined(_WIN32) #define Daemon i2p::util::DaemonWin32::Instance() class DaemonWin32 : public Daemon_Singleton diff --git a/HTTPServer.cpp b/HTTPServer.cpp index cfcf4261..39ced94a 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -377,7 +377,7 @@ namespace http { s << " Stop accepting tunnels
\r\n"; else s << " Start accepting tunnels
\r\n"; -#if (!defined(WIN32) && !defined(QT_GUI_LIB)) +#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) if (Daemon.gracefullShutdownInterval) { s << " Cancel gracefull shutdown ("; s << Daemon.gracefullShutdownInterval; @@ -690,12 +690,12 @@ namespace http { i2p::context.SetAcceptsTunnels (false); else if (cmd == HTTP_COMMAND_SHUTDOWN_START) { i2p::context.SetAcceptsTunnels (false); -#if (!defined(WIN32) && !defined(QT_GUI_LIB)) +#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) Daemon.gracefullShutdownInterval = 10*60; #endif } else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL) { i2p::context.SetAcceptsTunnels (true); -#if (!defined(WIN32) && !defined(QT_GUI_LIB)) +#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) Daemon.gracefullShutdownInterval = 0; #endif } else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW) { diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 00000000..8364f857 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,4 @@ +/gen/ +/libs/ +/tests/ +.idea diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml new file mode 100755 index 00000000..58d46ed8 --- /dev/null +++ b/android/AndroidManifest.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/android/jni/Android.mk b/android/jni/Android.mk new file mode 100755 index 00000000..bef6d5ec --- /dev/null +++ b/android/jni/Android.mk @@ -0,0 +1,115 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE := i2pd +LOCAL_CPP_FEATURES := rtti exceptions +LOCAL_C_INCLUDES += $(IFADDRS_PATH) ../.. +LOCAL_STATIC_LIBRARIES := \ + boost_system-gcc-mt-1_53 \ + boost_date_time-gcc-mt-1_53 \ + boost_filesystem-gcc-mt-1_53 \ + boost_program_options-gcc-mt-1_53 \ + crypto ssl \ + miniupnpc +LOCAL_LDLIBS := -lz + +#LOCAL_CFLAGS := +LOCAL_SRC_FILES := DaemonAndroid.cpp i2pd_android.cpp \ + $(IFADDRS_PATH)/ifaddrs.c \ + ../../HTTPServer.cpp ../../I2PControl.cpp ../../Daemon.cpp ../../Config.cpp \ + ../../AddressBook.cpp \ + ../../api.cpp \ + ../../Base.cpp \ + ../../BOB.cpp \ + ../../ClientContext.cpp \ + ../../Crypto.cpp \ + ../../Datagram.cpp \ + ../../Destination.cpp \ + ../../Family.cpp \ + ../../FS.cpp \ + ../../Garlic.cpp \ + ../../Gzip.cpp \ + ../../HTTP.cpp \ + ../../HTTPProxy.cpp \ + ../../I2CP.cpp \ + ../../I2NPProtocol.cpp \ + ../../I2PEndian.cpp \ + ../../I2PService.cpp \ + ../../I2PTunnel.cpp \ + ../../Identity.cpp \ + ../../LeaseSet.cpp \ + ../../Log.cpp \ + ../../NetDb.cpp \ + ../../NetDbRequests.cpp \ + ../../NTCPSession.cpp \ + ../../Profiling.cpp \ + ../../Reseed.cpp \ + ../../RouterContext.cpp \ + ../../RouterInfo.cpp \ + ../../SAM.cpp \ + ../../Signature.cpp \ + ../../SOCKS.cpp \ + ../../SSU.cpp \ + ../../SSUData.cpp \ + ../../SSUSession.cpp \ + ../../Streaming.cpp \ + ../../TransitTunnel.cpp \ + ../../Transports.cpp \ + ../../Tunnel.cpp \ + ../../TunnelEndpoint.cpp \ + ../../TunnelGateway.cpp \ + ../../TunnelPool.cpp \ + ../../UPnP.cpp \ + ../../util.cpp \ + ../../i2pd.cpp + +include $(BUILD_SHARED_LIBRARY) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE := boost_system-gcc-mt-1_53 +LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_53_0/$(TARGET_ARCH_ABI)/lib/libboost_system-gcc-mt-1_53.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_53_0/include +include $(PREBUILT_STATIC_LIBRARY) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE := boost_date_time-gcc-mt-1_53 +LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_53_0/$(TARGET_ARCH_ABI)/lib/libboost_date_time-gcc-mt-1_53.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_53_0/include +include $(PREBUILT_STATIC_LIBRARY) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE := boost_filesystem-gcc-mt-1_53 +LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_53_0/$(TARGET_ARCH_ABI)/lib/libboost_filesystem-gcc-mt-1_53.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_53_0/include +include $(PREBUILT_STATIC_LIBRARY) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE := boost_program_options-gcc-mt-1_53 +LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_53_0/$(TARGET_ARCH_ABI)/lib/libboost_program_options-gcc-mt-1_53.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_53_0/include +include $(PREBUILT_STATIC_LIBRARY) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE := crypto +LOCAL_SRC_FILES := $(OPENSSL_PATH)/openssl-1.0.2/$(TARGET_ARCH_ABI)/lib/libcrypto.a +LOCAL_EXPORT_C_INCLUDES := $(OPENSSL_PATH)/openssl-1.0.2/include +include $(PREBUILT_STATIC_LIBRARY) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE := ssl +LOCAL_SRC_FILES := $(OPENSSL_PATH)/openssl-1.0.2/$(TARGET_ARCH_ABI)/lib/libssl.a +LOCAL_EXPORT_C_INCLUDES := $(OPENSSL_PATH)/openssl-1.0.2/include +LOCAL_STATIC_LIBRARIES := crypto +include $(PREBUILT_STATIC_LIBRARY) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE := miniupnpc +LOCAL_SRC_FILES := $(MINIUPNP_PATH)/miniupnp-2.0/$(TARGET_ARCH_ABI)/lib/libminiupnpc.a +LOCAL_EXPORT_C_INCLUDES := $(MINIUPNP_PATH)/miniupnp-2.0/include +include $(PREBUILT_STATIC_LIBRARY) diff --git a/android/jni/Application.mk b/android/jni/Application.mk new file mode 100755 index 00000000..34f9dd63 --- /dev/null +++ b/android/jni/Application.mk @@ -0,0 +1,30 @@ +#APP_ABI := all +APP_ABI := armeabi-v7a x86 +#can be android-3 but will fail for x86 since arch-x86 is not present at ndkroot/platforms/android-3/ . libz is taken from there. +APP_PLATFORM := android-9 + +# http://stackoverflow.com/a/21386866/529442 http://stackoverflow.com/a/15616255/529442 to enable c++11 support in Eclipse +NDK_TOOLCHAIN_VERSION := 4.9 +# APP_STL := stlport_shared --> does not seem to contain C++11 features +APP_STL := gnustl_shared + +# Enable c++11 extentions in source code +APP_CPPFLAGS += -std=c++11 + +APP_CPPFLAGS += -DUSE_UPNP -DANDROID -D__ANDROID__ +ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) +APP_CPPFLAGS += -DANDROID_ARM7A +endif + +APP_OPTIM := debug + +# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git +# git clone https://github.com/PurpleI2P/OpenSSL-for-Android-Prebuilt.git +# git clone https://github.com/PurpleI2P/MiniUPnP-for-Android-Prebuilt.git +# git clone https://github.com/PurpleI2P/android-ifaddrs.git +# change to your own +I2PD_LIBS_PATH=/path/to/libraries +BOOST_PATH = $(I2PD_LIBS_PATH)/Boost-for-Android-Prebuilt +OPENSSL_PATH = $(I2PD_LIBS_PATH)/OpenSSL-for-Android-Prebuilt +MINIUPNP_PATH = $(I2PD_LIBS_PATH)/MiniUPnP-for-Android-Prebuilt +IFADDRS_PATH = $(I2PD_LIBS_PATH)/android-ifaddrs diff --git a/android/jni/DaemonAndroid.cpp b/android/jni/DaemonAndroid.cpp new file mode 100644 index 00000000..02f6e3f7 --- /dev/null +++ b/android/jni/DaemonAndroid.cpp @@ -0,0 +1,170 @@ +#include "DaemonAndroid.h" +#include "../../Daemon.h" +//#include "mainwindow.h" + +namespace i2p +{ +namespace android +{ +/* Worker::Worker (DaemonAndroidImpl& daemon): + m_Daemon (daemon) + { + } + + void Worker::startDaemon() + { + Log.d(TAG"Performing daemon start..."); + m_Daemon.start(); + Log.d(TAG"Daemon started."); + emit resultReady(); + } + void Worker::restartDaemon() + { + Log.d(TAG"Performing daemon restart..."); + m_Daemon.restart(); + Log.d(TAG"Daemon restarted."); + emit resultReady(); + } + void Worker::stopDaemon() { + Log.d(TAG"Performing daemon stop..."); + m_Daemon.stop(); + Log.d(TAG"Daemon stopped."); + emit resultReady(); + } + + Controller::Controller(DaemonAndroidImpl& daemon): + m_Daemon (daemon) + { + Worker *worker = new Worker (m_Daemon); + worker->moveToThread(&workerThread); + connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater); + connect(this, &Controller::startDaemon, worker, &Worker::startDaemon); + connect(this, &Controller::stopDaemon, worker, &Worker::stopDaemon); + connect(this, &Controller::restartDaemon, worker, &Worker::restartDaemon); + connect(worker, &Worker::resultReady, this, &Controller::handleResults); + workerThread.start(); + } + Controller::~Controller() + { + Log.d(TAG"Closing and waiting for daemon worker thread..."); + workerThread.quit(); + workerThread.wait(); + Log.d(TAG"Waiting for daemon worker thread finished."); + if(m_Daemon.isRunning()) + { + Log.d(TAG"Stopping the daemon..."); + m_Daemon.stop(); + Log.d(TAG"Stopped the daemon."); + } + } +*/ + DaemonAndroidImpl::DaemonAndroidImpl (): + /*mutex(nullptr), */ + m_IsRunning(false), + m_RunningChangedCallback(nullptr) + { + } + + DaemonAndroidImpl::~DaemonAndroidImpl () + { + //delete mutex; + } + + bool DaemonAndroidImpl::init(int argc, char* argv[]) + { + //mutex=new QMutex(QMutex::Recursive); + setRunningCallback(0); + m_IsRunning=false; + return Daemon.init(argc,argv); + } + + void DaemonAndroidImpl::start() + { + //QMutexLocker locker(mutex); + setRunning(true); + Daemon.start(); + } + + void DaemonAndroidImpl::stop() + { + //QMutexLocker locker(mutex); + Daemon.stop(); + setRunning(false); + } + + void DaemonAndroidImpl::restart() + { + //QMutexLocker locker(mutex); + stop(); + start(); + } + + void DaemonAndroidImpl::setRunningCallback(runningChangedCallback cb) + { + m_RunningChangedCallback = cb; + } + + bool DaemonAndroidImpl::isRunning() + { + return m_IsRunning; + } + + void DaemonAndroidImpl::setRunning(bool newValue) + { + bool oldValue = m_IsRunning; + if(oldValue!=newValue) + { + m_IsRunning = newValue; + if(m_RunningChangedCallback) + m_RunningChangedCallback(); + } + } + + static DaemonAndroidImpl daemon; + + /** + * returns 1 if daemon init failed + * returns 0 if daemon initialized and started okay + */ + int start(/*int argc, char* argv[]*/) + { + int result; + + { + //Log.d(TAG"Initialising the daemon..."); + bool daemonInitSuccess = daemon.init(0,0/*argc, argv*/); + if(!daemonInitSuccess) + { + //QMessageBox::critical(0, "Error", "Daemon init failed"); + return 1; + } + //Log.d(TAG"Initialised, creating the main window..."); + //MainWindow w; + //Log.d(TAG"Before main window.show()..."); + //w.show (); + + { + //i2p::qt::Controller daemonQtController(daemon); + //Log.d(TAG"Starting the daemon..."); + //emit daemonQtController.startDaemon(); + //daemon.start (); + //Log.d(TAG"Starting GUI event loop..."); + //result = app.exec(); + //daemon.stop (); + daemon.start(); + return 0; + } + } + + //QMessageBox::information(&w, "Debug", "demon stopped"); + //Log.d(TAG"Exiting the application"); + //return result; + } + + void stop() + { + daemon.stop(); + } +} +} + diff --git a/android/jni/DaemonAndroid.h b/android/jni/DaemonAndroid.h new file mode 100644 index 00000000..f6ee618f --- /dev/null +++ b/android/jni/DaemonAndroid.h @@ -0,0 +1,86 @@ +#ifndef DAEMON_ANDROID_H +#define DAEMON_ANDROID_H + +namespace i2p +{ +namespace android +{ + //FIXME currently NOT threadsafe + class DaemonAndroidImpl + { + public: + + DaemonAndroidImpl (); + ~DaemonAndroidImpl (); + + typedef void (*runningChangedCallback)(); + + /** + * @return success + */ + bool init(int argc, char* argv[]); + void start(); + void stop(); + void restart(); + void setRunningCallback(runningChangedCallback cb); + bool isRunning(); + private: + void setRunning(bool running); + private: + //QMutex* mutex; + bool m_IsRunning; + runningChangedCallback m_RunningChangedCallback; + }; + + /** + * returns 1 if daemon init failed + * returns 0 if daemon initialized and started okay + */ + int start(); + + // stops the daemon + void stop(); + + /* + class Worker : public QObject + { + Q_OBJECT + public: + + Worker (DaemonAndroidImpl& daemon); + + private: + + DaemonAndroidImpl& m_Daemon; + + public slots: + void startDaemon(); + void restartDaemon(); + void stopDaemon(); + + signals: + void resultReady(); + }; + + class Controller : public QObject + { + Q_OBJECT + QThread workerThread; + public: + Controller(DaemonAndroidImpl& daemon); + ~Controller(); + private: + DaemonAndroidImpl& m_Daemon; + + public slots: + void handleResults(){} + signals: + void startDaemon(); + void stopDaemon(); + void restartDaemon(); + }; + */ +} +} + +#endif // DAEMON_ANDROID_H diff --git a/android/jni/i2pd_android.cpp b/android/jni/i2pd_android.cpp new file mode 100755 index 00000000..b84ec1ac --- /dev/null +++ b/android/jni/i2pd_android.cpp @@ -0,0 +1,53 @@ + +//#include +#include +#include "org_purplei2p_i2pd_I2PD_JNI.h" +#include "DaemonAndroid.h" + +JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getABICompiledWith + (JNIEnv * env, jclass clazz) { +#if defined(__arm__) + #if defined(__ARM_ARCH_7A__) + #if defined(__ARM_NEON__) + #if defined(__ARM_PCS_VFP) + #define ABI "armeabi-v7a/NEON (hard-float)" + #else + #define ABI "armeabi-v7a/NEON" + #endif + #else + #if defined(__ARM_PCS_VFP) + #define ABI "armeabi-v7a (hard-float)" + #else + #define ABI "armeabi-v7a" + #endif + #endif + #else + #define ABI "armeabi" + #endif +#elif defined(__i386__) + #define ABI "x86" +#elif defined(__x86_64__) + #define ABI "x86_64" +#elif defined(__mips64) /* mips64el-* toolchain defines __mips__ too */ + #define ABI "mips64" +#elif defined(__mips__) + #define ABI "mips" +#elif defined(__aarch64__) + #define ABI "arm64-v8a" +#else + #define ABI "unknown" +#endif + + return env->NewStringUTF(ABI); +} + +JNIEXPORT jint JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startDaemon + (JNIEnv * env, jclass clazz) { + return (jint)i2p::android::start(); +} + +JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopDaemon + (JNIEnv * env, jclass clazz) { + i2p::android::stop(); +} + diff --git a/android/jni/org_purplei2p_i2pd_I2PD_JNI.h b/android/jni/org_purplei2p_i2pd_I2PD_JNI.h new file mode 100644 index 00000000..ddbcace8 --- /dev/null +++ b/android/jni/org_purplei2p_i2pd_I2PD_JNI.h @@ -0,0 +1,21 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class org_purplei2p_i2pd_I2PD_JNI */ + +#ifndef _Included_org_purplei2p_i2pd_I2PD_JNI +#define _Included_org_purplei2p_i2pd_I2PD_JNI +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_purplei2p_i2pd_I2PD_JNI + * Method: stringFromJNI + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getABICompiledWith + (JNIEnv *, jclass); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/android/project.properties b/android/project.properties new file mode 100644 index 00000000..c6998b3d --- /dev/null +++ b/android/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-9 diff --git a/android/res/drawable/icon.png b/android/res/drawable/icon.png new file mode 100644 index 00000000..a5dc7b68 Binary files /dev/null and b/android/res/drawable/icon.png differ diff --git a/android/res/drawable/itoopie_notification_icon.png b/android/res/drawable/itoopie_notification_icon.png new file mode 100644 index 00000000..8fbe2468 Binary files /dev/null and b/android/res/drawable/itoopie_notification_icon.png differ diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml new file mode 100755 index 00000000..b02181c8 --- /dev/null +++ b/android/res/values/strings.xml @@ -0,0 +1,4 @@ + + + i2pd + diff --git a/android/src/org/purplei2p/i2pd/I2PD.java b/android/src/org/purplei2p/i2pd/I2PD.java new file mode 100755 index 00000000..6f77c53f --- /dev/null +++ b/android/src/org/purplei2p/i2pd/I2PD.java @@ -0,0 +1,20 @@ +package org.purplei2p.i2pd; + +import android.app.Activity; +import android.widget.TextView; +import android.os.Bundle; + +public class I2PD extends Activity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + TextView tv = new TextView(this); + tv.setText( "libi2pd.so was compiled with ABI " + getABICompiledWith()); + setContentView(tv); + } + + public String getABICompiledWith() { + return I2PD_JNI.getABICompiledWith(); + } +} diff --git a/android/src/org/purplei2p/i2pd/I2PD_JNI.java b/android/src/org/purplei2p/i2pd/I2PD_JNI.java new file mode 100644 index 00000000..040cca1c --- /dev/null +++ b/android/src/org/purplei2p/i2pd/I2PD_JNI.java @@ -0,0 +1,16 @@ +package org.purplei2p.i2pd; + +public class I2PD_JNI { + public static native String getABICompiledWith(); + /** + * returns 1 if daemon init failed + * returns 0 if daemon initialized and started okay + */ + public static native int startDaemon(); + //should only be called after startDaemon() success + public static native void stopDaemon(); + + static { + System.loadLibrary("i2pd"); + } +}