Browse Source

revert switching from JNI to binary

Signed-off-by: r4sas <r4sas@i2pmail.org>
master
R4SAS 3 months ago
parent
commit
503f8a003e
Signed by: r4sas
GPG Key ID: 66F6C87B98EBCFE2
  1. 6
      .github/workflows/android.yml
  2. 7
      README.md
  3. 17
      app/build.gradle
  4. 80
      app/jni/Android.mk
  5. 23
      app/jni/Application.mk
  6. 138
      app/jni/DaemonAndroid.cpp
  7. 53
      app/jni/DaemonAndroid.h
  8. 141
      app/jni/i2pd_android.cpp
  9. 73
      app/jni/org_purplei2p_i2pd_I2PD_JNI.h
  10. 1
      app/src/main/AndroidManifest.xml
  11. 7
      app/src/main/java/org/purplei2p/i2pd/AbstractProcess.java
  12. 170
      app/src/main/java/org/purplei2p/i2pd/DaemonWrapper.java
  13. 9
      app/src/main/java/org/purplei2p/i2pd/ForegroundService.java
  14. 68
      app/src/main/java/org/purplei2p/i2pd/I2PDActivity.java
  15. 35
      app/src/main/java/org/purplei2p/i2pd/I2PD_JNI.java
  16. 154
      app/src/main/java/org/purplei2p/i2pd/I2pdApi.java
  17. 2
      app/src/main/java/org/purplei2p/i2pd/NetworkStateChangeReceiver.java
  18. 2
      app/src/main/java/org/purplei2p/i2pd/WebConsoleActivity.java
  19. 126
      app/src/main/java/org/purplei2p/i2pd/appscope/App.java
  20. 4
      app/src/main/res/layout/activity_main.xml
  21. 4
      app/src/main/res/menu/options_main.xml
  22. 14
      binary/jni/build.sh
  23. 0
      binary/libs/.gitkeep
  24. 2
      fastlane/metadata/android/en-US/changelogs/2530010.txt

6
.github/workflows/android.yml

@ -21,7 +21,7 @@ jobs:
run: | run: |
export ANDROID_NDK_HOME=$ANDROID_HOME/ndk/23.2.8568313 export ANDROID_NDK_HOME=$ANDROID_HOME/ndk/23.2.8568313
pushd binary/jni pushd binary/jni
./build.sh -md ./build.sh
popd popd
- name: Build with Gradle - name: Build with Gradle
run: ./gradlew --no-daemon assembleDebug run: ./gradlew --no-daemon assembleDebug
@ -45,11 +45,11 @@ jobs:
run: export JAVA_HOME=$JAVA_HOME_11_X64 run: export JAVA_HOME=$JAVA_HOME_11_X64
- name: Install required Android SDK packages - name: Install required Android SDK packages
run: $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager "cmake;3.22.1" "ndk;23.2.8568313" run: $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager "cmake;3.22.1" "ndk;23.2.8568313"
- name: Build binaries with NDK - name: Build binary with NDK
run: | run: |
export ANDROID_NDK_HOME=$ANDROID_HOME/ndk/23.2.8568313 export ANDROID_NDK_HOME=$ANDROID_HOME/ndk/23.2.8568313
pushd binary/jni pushd binary/jni
./build.sh ./build.sh -b
popd popd
- name: Create package with built binaries - name: Create package with built binaries
run: | run: |

7
README.md

@ -42,7 +42,6 @@ unzip commandlinetools-linux-8092744_latest.zip
```bash ```bash
git clone --recurse-submodules https://github.com/PurpleI2P/i2pd-android.git git clone --recurse-submodules https://github.com/PurpleI2P/i2pd-android.git
cd i2pd-android
``` ```
### Compile application ### Compile application
@ -54,8 +53,10 @@ export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
export ANDROID_HOME=/opt/android-sdk export ANDROID_HOME=/opt/android-sdk
export ANDROID_NDK_HOME=$ANDROID_HOME/ndk/23.2.8568313 export ANDROID_NDK_HOME=$ANDROID_HOME/ndk/23.2.8568313
pushd binary/jni pushd app/jni
./build.sh -md ./build_boost.sh
./build_openssl.sh
./build_miniupnpc.sh
popd popd
gradle clean assembleDebug gradle clean assembleDebug

17
app/build.gradle

@ -29,8 +29,8 @@ android {
targetSdkVersion 33 targetSdkVersion 33
// TODO: 24? // TODO: 24?
minSdkVersion 16 minSdkVersion 16
versionCode 2530000 versionCode 2530010
versionName "2.53.0" versionName "2.53.0.1"
archivesBaseName += "-$versionName" archivesBaseName += "-$versionName"
ndkVersion "23.2.8568313" ndkVersion "23.2.8568313"
@ -40,6 +40,13 @@ android {
abiFilters "arm64-v8a" abiFilters "arm64-v8a"
abiFilters "x86_64" abiFilters "x86_64"
} }
externalNativeBuild {
ndkBuild {
arguments "NDK_MODULE_PATH:=${rootProject.projectDir}/binary/jni"
arguments "-j${Runtime.getRuntime().availableProcessors()}"
}
}
} }
splits { splits {
@ -71,9 +78,9 @@ android {
} }
} }
sourceSets { externalNativeBuild {
main { ndkBuild {
jniLibs.srcDir file("${rootProject.projectDir}/binary/libs") path "${rootProject.projectDir}/app/jni/Android.mk"
} }
} }

80
app/jni/Android.mk

@ -0,0 +1,80 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := i2pd
LOCAL_CPP_FEATURES := rtti exceptions
LOCAL_C_INCLUDES += $(IFADDRS_PATH) $(LIB_SRC_PATH) $(LIB_CLIENT_SRC_PATH) $(LANG_SRC_PATH) $(DAEMON_SRC_PATH)
LOCAL_STATIC_LIBRARIES := \
boost_system \
boost_date_time \
boost_filesystem \
boost_program_options \
crypto \
ssl \
miniupnpc
LOCAL_LDLIBS := -lz
LOCAL_SRC_FILES := \
DaemonAndroid.cpp \
i2pd_android.cpp \
$(IFADDRS_PATH)/ifaddrs.cpp \
$(IFADDRS_PATH)/bionic_netlink.cpp \
$(wildcard $(LIB_SRC_PATH)/*.cpp) \
$(wildcard $(LIB_CLIENT_SRC_PATH)/*.cpp) \
$(wildcard $(LANG_SRC_PATH)/*.cpp) \
$(DAEMON_SRC_PATH)/Daemon.cpp \
$(DAEMON_SRC_PATH)/UPnP.cpp \
$(DAEMON_SRC_PATH)/HTTPServer.cpp \
$(DAEMON_SRC_PATH)/I2PControl.cpp \
$(DAEMON_SRC_PATH)/I2PControlHandlers.cpp
include $(BUILD_SHARED_LIBRARY)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := boost_system
LOCAL_SRC_FILES := $(BOOST_PATH)/build/out/$(TARGET_ARCH_ABI)/lib/libboost_system.a
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/build/out/$(TARGET_ARCH_ABI)/include
include $(PREBUILT_STATIC_LIBRARY)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := boost_date_time
LOCAL_SRC_FILES := $(BOOST_PATH)/build/out/$(TARGET_ARCH_ABI)/lib/libboost_date_time.a
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/build/out/$(TARGET_ARCH_ABI)/include
include $(PREBUILT_STATIC_LIBRARY)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := boost_filesystem
LOCAL_SRC_FILES := $(BOOST_PATH)/build/out/$(TARGET_ARCH_ABI)/lib/libboost_filesystem.a
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/build/out/$(TARGET_ARCH_ABI)/include
include $(PREBUILT_STATIC_LIBRARY)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := boost_program_options
LOCAL_SRC_FILES := $(BOOST_PATH)/build/out/$(TARGET_ARCH_ABI)/lib/libboost_program_options.a
LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/build/out/$(TARGET_ARCH_ABI)/include
include $(PREBUILT_STATIC_LIBRARY)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := crypto
LOCAL_SRC_FILES := $(OPENSSL_PATH)/out/$(TARGET_ARCH_ABI)/lib/libcrypto.a
LOCAL_EXPORT_C_INCLUDES := $(OPENSSL_PATH)/out/$(TARGET_ARCH_ABI)/include
include $(PREBUILT_STATIC_LIBRARY)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := ssl
LOCAL_SRC_FILES := $(OPENSSL_PATH)/out/$(TARGET_ARCH_ABI)/lib/libssl.a
LOCAL_EXPORT_C_INCLUDES := $(OPENSSL_PATH)/out/$(TARGET_ARCH_ABI)/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)/miniupnpc/out/$(TARGET_ARCH_ABI)/lib/libminiupnpc.a
LOCAL_EXPORT_C_INCLUDES := $(MINIUPNP_PATH)/miniupnpc/out/$(TARGET_ARCH_ABI)/include
include $(PREBUILT_STATIC_LIBRARY)

23
app/jni/Application.mk

@ -0,0 +1,23 @@
NDK_TOOLCHAIN_VERSION := clang
APP_STL := c++_static
# Enable c++17 extensions in source code
APP_CPPFLAGS += -std=c++17 -fexceptions -frtti
APP_CPPFLAGS += -DANDROID -D__ANDROID__ -DUSE_UPNP -Wno-deprecated-declarations
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
APP_CPPFLAGS += -DANDROID_ARM7A
endif
IFADDRS_PATH = $(NDK_MODULE_PATH)/android-ifaddrs
BOOST_PATH = $(NDK_MODULE_PATH)/boost
MINIUPNP_PATH = $(NDK_MODULE_PATH)/miniupnp
OPENSSL_PATH = $(NDK_MODULE_PATH)/openssl
# don't change me
I2PD_SRC_PATH = $(NDK_MODULE_PATH)/i2pd
LIB_SRC_PATH = $(I2PD_SRC_PATH)/libi2pd
LIB_CLIENT_SRC_PATH = $(I2PD_SRC_PATH)/libi2pd_client
LANG_SRC_PATH = $(I2PD_SRC_PATH)/i18n
DAEMON_SRC_PATH = $(I2PD_SRC_PATH)/daemon

138
app/jni/DaemonAndroid.cpp

@ -0,0 +1,138 @@
/*
* Copyright (c) 2013-2022, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include <iostream>
#include <chrono>
#include <thread>
#include <exception>
#include <boost/exception/diagnostic_information.hpp>
#include <boost/exception_ptr.hpp>
//#include "mainwindow.h"
#include "FS.h"
#include "DaemonAndroid.h"
#include "Daemon.h"
#include "I18N.h"
namespace i2p
{
namespace android
{
std::string dataDir = "";
std::string language = "";
DaemonAndroidImpl::DaemonAndroidImpl ()
{
}
DaemonAndroidImpl::~DaemonAndroidImpl ()
{
}
bool DaemonAndroidImpl::init(int argc, char* argv[])
{
return Daemon.init(argc, argv);
}
void DaemonAndroidImpl::start()
{
Daemon.start();
}
void DaemonAndroidImpl::stop()
{
Daemon.stop();
}
void DaemonAndroidImpl::restart()
{
stop();
start();
}
void DaemonAndroidImpl::setDataDir(std::string path)
{
Daemon.setDataDir(path);
}
static DaemonAndroidImpl daemon;
static char* argv[1]={strdup("tmp")};
/**
* returns error details if failed
* returns "ok" if daemon initialized and started okay
*/
std::string start(/*int argc, char* argv[]*/)
{
try
{
{
// make sure assets are ready before proceed
i2p::fs::DetectDataDir(dataDir, false);
int numAttempts = 0;
do
{
if (i2p::fs::Exists (i2p::fs::DataDirPath("assets.ready"))) break; // assets ready
numAttempts++;
std::this_thread::sleep_for (std::chrono::seconds(1)); // otherwise wait for 1 more second
}
while (numAttempts <= 10); // 10 seconds max
// Set application directory
daemon.setDataDir(dataDir);
bool daemonInitSuccess = daemon.init(1, argv);
if(!daemonInitSuccess)
{
return "Daemon init failed";
}
// Set webconsole language from application
i2p::i18n::SetLanguage(language);
daemon.start();
}
}
catch (boost::exception& ex)
{
std::stringstream ss;
ss << boost::diagnostic_information(ex);
return ss.str();
}
catch (std::exception& ex)
{
std::stringstream ss;
ss << ex.what();
return ss.str();
}
catch(...)
{
return "unknown exception";
}
return "ok";
}
void stop()
{
daemon.stop();
}
void SetDataDir(std::string jdataDir)
{
dataDir = jdataDir;
}
std::string GetDataDir(void)
{
return dataDir;
}
void SetLanguage(std::string jlanguage)
{
language = jlanguage;
}
}
}

53
app/jni/DaemonAndroid.h

@ -0,0 +1,53 @@
/*
* Copyright (c) 2013-2022, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#ifndef DAEMON_ANDROID_H
#define DAEMON_ANDROID_H
#include <string>
namespace i2p
{
namespace android
{
class DaemonAndroidImpl
{
public:
DaemonAndroidImpl ();
~DaemonAndroidImpl ();
/**
* @return success
*/
bool init (int argc, char* argv[]);
void start ();
void stop ();
void restart ();
void setDataDir (std::string path);
};
/**
* returns "ok" if daemon init failed
* returns errinfo if daemon initialized and started okay
*/
std::string start ();
void stop ();
// set datadir received from jni
void SetDataDir (std::string jdataDir);
// get datadir
std::string GetDataDir (void);
// set webconsole language
void SetLanguage (std::string jlanguage);
}
}
#endif // DAEMON_ANDROID_H

141
app/jni/i2pd_android.cpp

@ -0,0 +1,141 @@
/*
* Copyright (c) 2013-2022, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
#include <jni.h>
#include "org_purplei2p_i2pd_I2PD_JNI.h"
#include "DaemonAndroid.h"
#include "Config.h"
#include "RouterContext.h"
#include "ClientContext.h"
#include "Transports.h"
#include "Tunnel.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 jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startDaemon
(JNIEnv *env, jclass clazz) {
return env->NewStringUTF(i2p::android::start().c_str());
}
JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getDataDir
(JNIEnv *env, jclass clazz) {
return env->NewStringUTF(i2p::android::GetDataDir().c_str());
}
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopDaemon
(JNIEnv *env, jclass clazz) {
i2p::android::stop();
}
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopAcceptingTunnels
(JNIEnv *env, jclass clazz) {
i2p::context.SetAcceptsTunnels (false);
}
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startAcceptingTunnels
(JNIEnv *env, jclass clazz) {
i2p::context.SetAcceptsTunnels (true);
}
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_reloadTunnelsConfigs
(JNIEnv *env, jclass clazz) {
i2p::client::context.ReloadConfig();
}
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_onNetworkStateChanged
(JNIEnv *env, jclass clazz, jboolean isConnected) {
bool isConnectedBool = (bool) isConnected;
i2p::transport::transports.SetOnline (isConnectedBool);
}
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_setDataDir
(JNIEnv *env, jclass clazz, jstring jdataDir) {
auto dataDir = env->GetStringUTFChars(jdataDir, NULL);
i2p::android::SetDataDir(dataDir);
env->ReleaseStringUTFChars(jdataDir, dataDir);
}
JNIEXPORT jint JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getTransitTunnelsCount
(JNIEnv *env, jclass clazz) {
return i2p::tunnel::tunnels.CountTransitTunnels();
}
JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getWebConsAddr
(JNIEnv *env, jclass clazz) {
std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
std::string result = "http://" + httpAddr + ":" + std::to_string(httpPort) + "/";
return env->NewStringUTF(result.c_str());
}
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_setLanguage
(JNIEnv *env, jclass clazz, jstring jlanguage) {
auto language = env->GetStringUTFChars(jlanguage, NULL);
i2p::android::SetLanguage(language);
env->ReleaseStringUTFChars(jlanguage, language);
}
JNIEXPORT jboolean JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getHTTPProxyState
(JNIEnv *, jclass) {
return i2p::client::context.GetHttpProxy () ? true : false;
}
JNIEXPORT jboolean JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getSOCKSProxyState
(JNIEnv *, jclass) {
return i2p::client::context.GetSocksProxy() ? true : false;
}
JNIEXPORT jboolean JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getBOBState
(JNIEnv *, jclass) {
return i2p::client::context.GetBOBCommandChannel() ? true : false;
}
JNIEXPORT jboolean JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getSAMState
(JNIEnv *, jclass) {
return i2p::client::context.GetSAMBridge() ? true : false;
}
JNIEXPORT jboolean JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getI2CPState
(JNIEnv *, jclass) {
return i2p::client::context.GetI2CPServer() ? true : false;
}

73
app/jni/org_purplei2p_i2pd_I2PD_JNI.h

@ -0,0 +1,73 @@
/*
* Copyright (c) 2013-2022, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* 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
JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getABICompiledWith
(JNIEnv *, jclass);
JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startDaemon
(JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopDaemon
(JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startAcceptingTunnels
(JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopAcceptingTunnels
(JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_reloadTunnelsConfigs
(JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_setDataDir
(JNIEnv *env, jclass clazz, jstring jdataDir);
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_setLanguage
(JNIEnv *env, jclass clazz, jstring jlanguage);
JNIEXPORT jint JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getTransitTunnelsCount
(JNIEnv *, jclass);
JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getWebConsAddr
(JNIEnv *, jclass);
JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getDataDir
(JNIEnv *, jclass);
JNIEXPORT jboolean JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getHTTPProxyState
(JNIEnv *, jclass);
JNIEXPORT jboolean JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getSOCKSProxyState
(JNIEnv *, jclass);
JNIEXPORT jboolean JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getBOBState
(JNIEnv *, jclass);
JNIEXPORT jboolean JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getSAMState
(JNIEnv *, jclass) ;
JNIEXPORT jboolean JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getI2CPState
(JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_onNetworkStateChanged
(JNIEnv * env, jclass clazz, jboolean isConnected);
#ifdef __cplusplus
}
#endif
#endif

1
app/src/main/AndroidManifest.xml

@ -16,7 +16,6 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application <application
android:name=".appscope.App"
android:allowBackup="true" android:allowBackup="true"
android:icon="@mipmap/logo" android:icon="@mipmap/logo"
android:label="@string/app_name" android:label="@string/app_name"

7
app/src/main/java/org/purplei2p/i2pd/AbstractProcess.java

@ -1,7 +0,0 @@
package org.purplei2p.i2pd;
public interface AbstractProcess {
/** @param tr can be null
*/
void kill(Throwable tr);
}

170
app/src/main/java/org/purplei2p/i2pd/DaemonWrapper.java

@ -13,7 +13,6 @@ import java.util.Set;
import java.util.Locale; import java.util.Locale;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.AssetManager; import android.content.res.AssetManager;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.Network; import android.net.Network;
@ -68,25 +67,25 @@ public class DaemonWrapper {
public synchronized void stopAcceptingTunnels() { public synchronized void stopAcceptingTunnels() {
if (isStartedOkay()) { if (isStartedOkay()) {
setState(State.gracefulShutdownInProgress); setState(State.gracefulShutdownInProgress);
//I2PD_JNI.stopAcceptingTunnels(); I2PD_JNI.stopAcceptingTunnels();
} }
} }
public synchronized void startAcceptingTunnels() { public synchronized void startAcceptingTunnels() {
if (isStartedOkay()) { if (isStartedOkay()) {
setState(State.startedOkay); setState(State.startedOkay);
//I2PD_JNI.startAcceptingTunnels(); I2PD_JNI.startAcceptingTunnels();
} }
} }
public synchronized void reloadTunnelsConfigs() { public synchronized void reloadTunnelsConfigs() {
if (isStartedOkay()) { if (isStartedOkay()) {
//I2PD_JNI.reloadTunnelsConfigs(); I2PD_JNI.reloadTunnelsConfigs();
} }
} }
public int getTransitTunnelsCount() { public int getTransitTunnelsCount() {
return 0;//I2PD_JNI.getTransitTunnelsCount(); return I2PD_JNI.getTransitTunnelsCount();
} }
public enum State { public enum State {
@ -118,11 +117,11 @@ public class DaemonWrapper {
return state; return state;
} }
public DaemonWrapper(Context ctx, AssetManager assetManager, ConnectivityManager connectivityManager){ public DaemonWrapper(AssetManager assetManager, ConnectivityManager connectivityManager){
this.assetManager = assetManager; this.assetManager = assetManager;
this.connectivityManager = connectivityManager; this.connectivityManager = connectivityManager;
setState(State.starting); setState(State.starting);
//startDaemon(ctx); //need to start when storage permissions to the datadir exist startDaemon();
} }
private Throwable lastThrowable; private Throwable lastThrowable;
@ -148,13 +147,11 @@ public class DaemonWrapper {
} }
public static String getDataDir() { // for settings iniEditor public static String getDataDir() { // for settings iniEditor
return i2pdDataDir; return I2PD_JNI.getDataDir();
} }
private static String i2pdDataDir;
public void changeDataDir(String dataDir, Boolean updateAssets) { public void changeDataDir(String dataDir, Boolean updateAssets) {
i2pdDataDir=dataDir; I2PD_JNI.setDataDir(dataDir);
if (updateAssets) processAssets(); if (updateAssets) processAssets();
//ToDo: move old dir to new dir? //ToDo: move old dir to new dir?
} }
@ -163,44 +160,44 @@ public class DaemonWrapper {
return getState().isStartedOkay(); return getState().isStartedOkay();
} }
public synchronized void stopDaemon(final Throwable throwable) { public synchronized void stopDaemon() {
if (isStartedOkay()) { if (isStartedOkay()) {
try { try {
I2pdApi.stopDaemon(throwable); I2PD_JNI.stopDaemon();
} catch (Throwable tr) { } catch (Throwable tr) {
Log.e(TAG, "", tr); Log.e(TAG, "", tr);
} }
if (throwable != null) lastThrowable = throwable;
setState(State.stopped); setState(State.stopped);
} }
} }
public synchronized void startDaemon() {
public synchronized void startDaemonIfStopped(Context ctx) { if( getState() != State.stopped && getState() != State.starting ) return;
if (getState() == State.startedOkay) return;
new Thread(() -> { new Thread(() -> {
synchronized(DaemonWrapper.this) { try {
if (getState() == State.startedOkay) return; processAssets();
try { I2PD_JNI.loadLibraries();
processAssets(); //registerNetworkCallback();
//registerNetworkCallback(); } catch (Throwable tr) {
} catch (Throwable tr) { lastThrowable = tr;
lastThrowable = tr; setState(State.startFailed);
setState(State.startFailed); return;
return; }
} try {
try { synchronized (DaemonWrapper.this) {
String locale = getAppLocale(); I2PD_JNI.setDataDir(i2pdpath); // (Environment.getExternalStorageDirectory().getAbsolutePath() + "/i2pd");
Log.i(TAG, "setting webconsole language to " + locale);
Log.i(TAG, "setting webconsole language to " + appLocale);
I2PD_JNI.setLanguage(appLocale);
daemonStartResult = I2pdApi.startDaemon(ctx, i2pdpath, locale, DaemonWrapper.this); daemonStartResult = I2PD_JNI.startDaemon();
if ("ok".equals(daemonStartResult)) { if ("ok".equals(daemonStartResult)) {
setState(State.startedOkay); setState(State.startedOkay);
} else } else
setState(State.startFailed); setState(State.startFailed);
} catch (Throwable tr) {
lastThrowable = tr;
setState(State.startFailed);
} }
} catch (Throwable tr) {
lastThrowable = tr;
setState(State.startFailed);
} }
}, "i2pdDaemonStart").start(); }, "i2pdDaemonStart").start();
} }
@ -210,75 +207,71 @@ public class DaemonWrapper {
String versionName = BuildConfig.VERSION_NAME; // here will be app version, like 2.XX.XX String versionName = BuildConfig.VERSION_NAME; // here will be app version, like 2.XX.XX
StringBuilder text = new StringBuilder(); StringBuilder text = new StringBuilder();
Log.d(TAG, "checking assets"); Log.d(TAG, "checking assets");
try {
if (holderFile.exists()) { if (holderFile.exists()) {
try { // if holder file exists, read assets version string try { // if holder file exists, read assets version string
FileReader fileReader = new FileReader(holderFile); FileReader fileReader = new FileReader(holderFile);
try {
BufferedReader br = new BufferedReader(fileReader);
try { try {
BufferedReader br = new BufferedReader(fileReader); String line;
try { while ((line = br.readLine()) != null) {
String line; text.append(line);
while ((line = br.readLine()) != null) {
text.append(line);
}
} finally {
try {
br.close();
} catch (IOException e) {
Log.e(TAG, "", e);
}
} }
} finally { }finally {
try { try {
fileReader.close(); br.close();
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "", e); Log.e(TAG, "", e);
} }
} }
} catch (IOException e) { } finally {
Log.e(TAG, "", e); try {
fileReader.close();
} catch (IOException e) {
Log.e(TAG, "", e);
}
} }
} catch (IOException e) {
Log.e(TAG, "", e);
} }
}
// if version differs from current app version or null, try to delete certificates folder // if version differs from current app version or null, try to delete certificates folder
if (!text.toString().contains(versionName)) { if (!text.toString().contains(versionName)) {
try {
boolean deleteResult = holderFile.delete();
if (!deleteResult)
Log.e(TAG, "holderFile.delete() returned " + deleteResult + ", absolute path='" + holderFile.getAbsolutePath() + "'");
File certPath = new File(i2pdpath, "certificates");
deleteRecursive(certPath);
// copy assets. If processed file exists, it won't be overwritten
copyAsset("addressbook");
copyAsset("certificates");
copyAsset("tunnels.d");
copyAsset("i2pd.conf");
copyAsset("tunnels.conf");
// update holder file about successful copying
FileWriter writer = new FileWriter(holderFile);
try { try {
boolean deleteResult = holderFile.delete(); writer.append(versionName);
if (!deleteResult) } finally {
Log.e(TAG, "holderFile.delete() returned " + deleteResult + ", absolute path='" + holderFile.getAbsolutePath() + "'");
File certPath = new File(i2pdpath, "certificates");
deleteRecursive(certPath);
// copy assets. If processed file exists, it won't be overwritten
copyAsset("addressbook");
copyAsset("certificates");
copyAsset("tunnels.d");
copyAsset("i2pd.conf");
copyAsset("tunnels.conf");
// update holder file about successful copying
FileWriter writer = new FileWriter(holderFile);
try { try {
writer.append(versionName); writer.close();
} finally { } catch (IOException e) {
try { Log.e(TAG,"on writer close", e);
writer.close();
} catch (IOException e) {
Log.e(TAG, "on writer close", e);
}
} }
} catch (Throwable tr) {
Log.e(TAG, "on assets copying", tr);
} }
} }
}catch(Throwable tr) { catch (Throwable tr)
if(tr.getMessage().contains("Permission denied")) { {
Log.e(TAG, "Permission denied on assets copying", tr); Log.e(TAG,"on assets copying", tr);
} }
throw new RuntimeException(tr);
} }
} }
@ -378,18 +371,15 @@ public class DaemonWrapper {
@Override @Override
public void onAvailable(Network network) { public void onAvailable(Network network) {
super.onAvailable(network); super.onAvailable(network);
//I2PD_JNI.onNetworkStateChanged(true); I2PD_JNI.onNetworkStateChanged(true);
Log.d(TAG, "NetworkCallback.onAvailable"); Log.d(TAG, "NetworkCallback.onAvailable");
} }
@Override @Override
public void onLost(Network network) { public void onLost(Network network) {
super.onLost(network); super.onLost(network);
//I2PD_JNI.onNetworkStateChanged(false); I2PD_JNI.onNetworkStateChanged(false);
Log.d(TAG, " NetworkCallback.onLost"); Log.d(TAG, " NetworkCallback.onLost");
} }
} }
private String getAppLocale() {
return Locale.getDefault().getDisplayLanguage(Locale.ENGLISH).toLowerCase(); // lower-case system language (like "english")
}
} }

9
app/src/main/java/org/purplei2p/i2pd/ForegroundService.java

@ -19,11 +19,6 @@ import android.util.Log;
public class ForegroundService extends Service { public class ForegroundService extends Service {
private static final String TAG = "FgService"; private static final String TAG = "FgService";
private volatile boolean shown; private volatile boolean shown;
public static ForegroundService getInstance() {
return instance;
}
private static ForegroundService instance; private static ForegroundService instance;
private static volatile DaemonWrapper daemon; private static volatile DaemonWrapper daemon;
private static final Object initDeinitLock = new Object(); private static final Object initDeinitLock = new Object();
@ -97,10 +92,6 @@ public class ForegroundService extends Service {
@Override @Override
public void onDestroy() { public void onDestroy() {
stop();
}
public void stop() {
cancelNotification(); cancelNotification();
deinitCheck(); deinitCheck();
instance = null; instance = null;

68
app/src/main/java/org/purplei2p/i2pd/I2PDActivity.java

@ -38,8 +38,6 @@ import androidx.core.content.ContextCompat;
import static android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS; import static android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS;
import org.purplei2p.i2pd.appscope.App;
public class I2PDActivity extends Activity { public class I2PDActivity extends Activity {
private static final String TAG = "i2pdActvt"; private static final String TAG = "i2pdActvt";
private static final int MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 1; private static final int MY_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
@ -47,12 +45,14 @@ public class I2PDActivity extends Activity {
public static final String PACKAGE_URI_SCHEME = "package:"; public static final String PACKAGE_URI_SCHEME = "package:";
private TextView textView; private TextView textView;
/*private CheckBox HTTPProxyState; private CheckBox HTTPProxyState;
private CheckBox SOCKSProxyState; private CheckBox SOCKSProxyState;
private CheckBox BOBState; private CheckBox BOBState;
private CheckBox SAMState; private CheckBox SAMState;
private CheckBox I2CPState; private CheckBox I2CPState;
*/
private static volatile DaemonWrapper daemon;
private final DaemonWrapper.StateUpdateListener daemonStateUpdatedListener = new DaemonWrapper.StateUpdateListener() { private final DaemonWrapper.StateUpdateListener daemonStateUpdatedListener = new DaemonWrapper.StateUpdateListener() {
@Override @Override
@ -60,10 +60,6 @@ public class I2PDActivity extends Activity {
updateStatusText(); updateStatusText();
} }
}; };
private DaemonWrapper getDaemon() {
return App.getDaemonWrapper();
}
private void updateStatusText() { private void updateStatusText() {
runOnUiThread(() -> { runOnUiThread(() -> {
@ -71,25 +67,23 @@ public class I2PDActivity extends Activity {
if (textView == null) if (textView == null)
return; return;
Throwable tr = getDaemon().getLastThrowable(); Throwable tr = daemon.getLastThrowable();
if (tr != null) { if (tr != null) {
textView.setText(throwableToString(tr)); textView.setText(throwableToString(tr));
return; return;
} }
DaemonWrapper.State state = getDaemon().getState(); DaemonWrapper.State state = daemon.getState();
if (getDaemon().isStartedOkay()) { if (daemon.isStartedOkay()) {
/*
HTTPProxyState.setChecked(I2PD_JNI.getHTTPProxyState()); HTTPProxyState.setChecked(I2PD_JNI.getHTTPProxyState());
SOCKSProxyState.setChecked(I2PD_JNI.getSOCKSProxyState()); SOCKSProxyState.setChecked(I2PD_JNI.getSOCKSProxyState());
BOBState.setChecked(I2PD_JNI.getBOBState()); BOBState.setChecked(I2PD_JNI.getBOBState());
SAMState.setChecked(I2PD_JNI.getSAMState()); SAMState.setChecked(I2PD_JNI.getSAMState());
I2CPState.setChecked(I2PD_JNI.getI2CPState()); I2CPState.setChecked(I2PD_JNI.getI2CPState());
*/
} }
String startResultStr = DaemonWrapper.State.startFailed==state ? String.format(": %s", getDaemon().getDaemonStartResult()) : ""; String startResultStr = DaemonWrapper.State.startFailed.equals(state) ? String.format(": %s", daemon.getDaemonStartResult()) : "";
String graceStr = DaemonWrapper.State.gracefulShutdownInProgress.equals(state) ? String.format(": %s %s", formatGraceTimeRemaining(), getText(R.string.remaining)) : ""; String graceStr = DaemonWrapper.State.gracefulShutdownInProgress.equals(state) ? String.format(": %s %s", formatGraceTimeRemaining(), getText(R.string.remaining)) : "";
textView.setText(String.format("%s%s%s", getText(state.getStatusStringResourceId()), startResultStr, graceStr)); textView.setText(String.format("%s%s%s", getText(state.getStatusStringResourceId()), startResultStr, graceStr));
} catch (Throwable tr) { } catch (Throwable tr) {
@ -119,21 +113,20 @@ public class I2PDActivity extends Activity {
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
startService(new Intent(this, ForegroundService.class)); startService(new Intent(this, ForegroundService.class));
textView = (TextView) findViewById(R.id.appStatusText); textView = (TextView) findViewById(R.id.appStatusText);
/*
HTTPProxyState = (CheckBox) findViewById(R.id.service_httpproxy_box); HTTPProxyState = (CheckBox) findViewById(R.id.service_httpproxy_box);
SOCKSProxyState = (CheckBox) findViewById(R.id.service_socksproxy_box); SOCKSProxyState = (CheckBox) findViewById(R.id.service_socksproxy_box);
BOBState = (CheckBox) findViewById(R.id.service_bob_box); BOBState = (CheckBox) findViewById(R.id.service_bob_box);
SAMState = (CheckBox) findViewById(R.id.service_sam_box); SAMState = (CheckBox) findViewById(R.id.service_sam_box);
I2CPState = (CheckBox) findViewById(R.id.service_i2cp_box);*/ I2CPState = (CheckBox) findViewById(R.id.service_i2cp_box);
/*if (getDaemon() == null) { if (daemon == null) {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
getDaemon() = new getDaemon()Wrapper(getAssets(), connectivityManager); daemon = new DaemonWrapper(getAssets(), connectivityManager);
} }
ForegroundService.init(getDaemon()); ForegroundService.init(daemon);
*/ daemon.addStateChangeListener(daemonStateUpdatedListener);
//getDaemon()StateUpdatedListener.getDaemon()StateUpdate(getDaemon()Wrapper.State.uninitialized, App.getgetDaemon()Wrapper().getState()); daemonStateUpdatedListener.daemonStateUpdate(DaemonWrapper.State.uninitialized, daemon.getState());
// request permissions // request permissions
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
@ -149,11 +142,6 @@ public class I2PDActivity extends Activity {
} }
} }
//here, we might have datadir access permissions,
// it's maybe safe to call daemon start which copies i2pd assets
getDaemon().startDaemonIfStopped(getApplicationContext());
getDaemon().addStateChangeListener(daemonStateUpdatedListener);
updateStatusText();
doBindService(); doBindService();
final Timer gracefulQuitTimer = getGracefulQuitTimer(); final Timer gracefulQuitTimer = getGracefulQuitTimer();
@ -173,7 +161,7 @@ public class I2PDActivity extends Activity {
super.onDestroy(); super.onDestroy();
textView = null; textView = null;
ForegroundService.deinit(); ForegroundService.deinit();
getDaemon().removeStateChangeListener(daemonStateUpdatedListener); daemon.removeStateChangeListener(daemonStateUpdatedListener);
//cancelGracefulStop0(); //cancelGracefulStop0();
try { try {
doUnbindService(); doUnbindService();
@ -306,15 +294,15 @@ public class I2PDActivity extends Activity {
onActionBatteryOptimizations(); onActionBatteryOptimizations();
return true; return true;
/* case R.id.action_reload_tunnels_config: case R.id.action_reload_tunnels_config:
onReloadTunnelsConfig(); onReloadTunnelsConfig();
return true;*/ return true;
case R.id.action_start_webview: case R.id.action_start_webview:
if(getDaemon().isStartedOkay()) if(daemon.isStartedOkay())
startActivity(new Intent(getApplicationContext(), WebConsoleActivity.class)); startActivity(new Intent(getApplicationContext(), WebConsoleActivity.class));
else else
Toast.makeText(this,"I2Pd not started!", Toast.LENGTH_SHORT).show(); Toast.makeText(this,"I2Pd not was started!", Toast.LENGTH_SHORT).show();
return true; return true;
case R.id.action_settings: case R.id.action_settings:
startActivity(new Intent(getApplicationContext(), SettingsActivity.class)); startActivity(new Intent(getApplicationContext(), SettingsActivity.class));
@ -337,7 +325,7 @@ public class I2PDActivity extends Activity {
private void onReloadTunnelsConfig() { private void onReloadTunnelsConfig() {
Log.i(TAG, "reloading tunnels"); Log.i(TAG, "reloading tunnels");
getDaemon().reloadTunnelsConfigs(); daemon.reloadTunnelsConfigs();
Toast.makeText(this, R.string.tunnels_reloading, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.tunnels_reloading, Toast.LENGTH_SHORT).show();
} }
@ -347,7 +335,7 @@ public class I2PDActivity extends Activity {
textView.setText(getText(R.string.stopping)); textView.setText(getText(R.string.stopping));
new Thread(() -> { new Thread(() -> {
try { try {
getDaemon().stopDaemon(null); daemon.stopDaemon();
} catch (Throwable tr) { } catch (Throwable tr) {
Log.e(TAG, "", tr); Log.e(TAG, "", tr);
} }
@ -358,7 +346,7 @@ public class I2PDActivity extends Activity {
private static volatile Timer gracefulQuitTimer; private static volatile Timer gracefulQuitTimer;
private void i2pdGracefulStop() { private void i2pdGracefulStop() {
if (getDaemon().getState() == DaemonWrapper.State.stopped) { if (daemon.getState() == DaemonWrapper.State.stopped) {
Toast.makeText(this, R.string.already_stopped, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.already_stopped, Toast.LENGTH_SHORT).show();
return; return;
} }
@ -370,8 +358,8 @@ public class I2PDActivity extends Activity {
Toast.makeText(this, R.string.graceful_stop_is_in_progress, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.graceful_stop_is_in_progress, Toast.LENGTH_SHORT).show();
new Thread(() -> { new Thread(() -> {
try { try {
if (getDaemon().isStartedOkay()) { if (daemon.isStartedOkay()) {
getDaemon().stopAcceptingTunnels(); daemon.stopAcceptingTunnels();
long gracefulStopAtMillis; long gracefulStopAtMillis;
synchronized (graceStartedMillis_LOCK) { synchronized (graceStartedMillis_LOCK) {
graceStartedMillis = System.currentTimeMillis(); graceStartedMillis = System.currentTimeMillis();
@ -392,8 +380,8 @@ public class I2PDActivity extends Activity {
Log.i(TAG, "canceling graceful stop"); Log.i(TAG, "canceling graceful stop");
new Thread(() -> { new Thread(() -> {
try { try {
if (getDaemon().isStartedOkay()) { if (daemon.isStartedOkay()) {
getDaemon().startAcceptingTunnels(); daemon.startAcceptingTunnels();
runOnUiThread(() -> Toast.makeText(this, R.string.shutdown_canceled, Toast.LENGTH_SHORT).show()); runOnUiThread(() -> Toast.makeText(this, R.string.shutdown_canceled, Toast.LENGTH_SHORT).show());
} else } else
i2pdStop(); i2pdStop();
@ -407,7 +395,7 @@ public class I2PDActivity extends Activity {
if (gracefulQuitTimerOld != null) if (gracefulQuitTimerOld != null)
gracefulQuitTimerOld.cancel(); gracefulQuitTimerOld.cancel();
if (getDaemon().getTransitTunnelsCount() <= 0) { // no tunnels left if (daemon.getTransitTunnelsCount() <= 0) { // no tunnels left
Log.i(TAG, "no transit tunnels left, stopping"); Log.i(TAG, "no transit tunnels left, stopping");
i2pdStop(); i2pdStop();
return; return;
@ -519,7 +507,7 @@ public class I2PDActivity extends Activity {
Log.e(TAG, "", tr); Log.e(TAG, "", tr);
} }
try { try {
((App)getApplication()).quit(); daemon.stopDaemon();
} catch (Throwable tr) { } catch (Throwable tr) {
Log.e(TAG, "", tr); Log.e(TAG, "", tr);
} }

35
app/src/main/java/org/purplei2p/i2pd/I2PD_JNI.java

@ -0,0 +1,35 @@
package org.purplei2p.i2pd;
public class I2PD_JNI {
public static native String getABICompiledWith();
public static void loadLibraries() {
System.loadLibrary("i2pd");
}
/**
* returns error info if failed
* returns "ok" if daemon initialized and started okay
*/
public static native String startDaemon();
public static native void stopDaemon();
public static native void startAcceptingTunnels();
public static native void stopAcceptingTunnels();
public static native void reloadTunnelsConfigs();
public static native void setDataDir(String jdataDir);
public static native void setLanguage(String jlanguage);
public static native int getTransitTunnelsCount();
public static native String getWebConsAddr();
public static native String getDataDir();
public static native boolean getHTTPProxyState();
public static native boolean getSOCKSProxyState();
public static native boolean getBOBState();
public static native boolean getSAMState();
public static native boolean getI2CPState();
public static native void onNetworkStateChanged(boolean isConnected);
}

154
app/src/main/java/org/purplei2p/i2pd/I2pdApi.java

@ -1,154 +0,0 @@
package org.purplei2p.i2pd;
import android.content.Context;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
/** i2pd process API calls via TCP between the Android Java app and i2pd C++-only process.
* TODO
*/
public class I2pdApi {
private static String dataDir;
private static AbstractProcess i2pdProcess;
private static final String TAG = "I2pdApi";
/**
* returns error info if failed
* returns "ok" if daemon initialized and started okay
*/
public static String startDaemon(final Context ctx, final String dataDir, String ignoredLanguage, final DaemonWrapper daemonWrapper){
try {
i2pdProcess = null;
I2pdApi.dataDir = dataDir;
File pidFile = new File(dataDir, "i2pd.pid");
Log.i(TAG,"Launching an i2pd process");
final Process p = Runtime.getRuntime().exec(new String[]{
"/system/bin/sh",
"-c",
"ulimit -c unlimited && "+
ctx.getApplicationInfo().nativeLibraryDir +
"/libi2pd.so --datadir=" + dataDir +
" --pidfile=" + pidFile.getAbsolutePath() +
" > "+dataDir+"/s.log 2>&1"
});
i2pdProcess = (Throwable tr) -> {
try {
if (tr != null)
Log.e(TAG, "destroying the subprocess \"i2pd\", reason: " + tr, tr);
else
Log.e(TAG, "destroying the subprocess \"i2pd\", reason: null");
p.destroy();
} catch (Throwable tr2) {
Log.e(TAG, "", tr2);
}
};
new Thread(() -> {
try {
try (BufferedInputStream bis = new BufferedInputStream(p.getInputStream())) {
try (InputStreamReader sr = new InputStreamReader(bis)) {
try (BufferedReader r = new BufferedReader(sr)) {
while (true) {
String s = r.readLine();
if (s == null) break;
Log.i(TAG, s);
}
}
}
}
} catch (Throwable tr) {
Log.e(TAG, "", tr);
}
}, "i2pd-stdout").start();
new Thread(() -> {
try {
try (BufferedInputStream bis = new BufferedInputStream(p.getErrorStream())) {
try (InputStreamReader sr = new InputStreamReader(bis)) {
try (BufferedReader r = new BufferedReader(sr)) {
while (true) {
String s = r.readLine();
if (s == null) break;
Log.i(TAG, s);
}
}
}
}
} catch (Throwable tr) {
Log.e(TAG, "", tr);
}
try {
p.waitFor();
} catch (Throwable tr) {
Log.e(TAG, "", tr);
}
final int errorLevel = p.exitValue();
Log.i(TAG, "i2pd process exit code: " + errorLevel);
final Throwable trReason = new Throwable("subprocess \"i2pd\" exited with exit code " + errorLevel);
try {
stopDaemon(trReason);
Log.i(TAG, "stopDaemon completed");
} catch (Throwable tr) {
Log.e(TAG, "Called stopDaemon, got exception", tr);
}
new Thread(() -> {
try {
daemonWrapper.stopDaemon(trReason);
Log.i(TAG, "daemonWrapper.stopDaemon completed");
} catch (Throwable tr) {
Log.e(TAG, "Called daemonWrapper.stopDaemon, got exception", tr);
}
}, "stop the daemonWrapper thread").start();
}, "i2pd-stderr").start();
new Thread(() -> {
try {
// try (BufferedOutputStream bos = new BufferedOutputStream(p.getOutputStream())) {
// try (OutputStreamWriter sr = new OutputStreamWriter(bos)) {
// try (BufferedWriter r = new BufferedWriter(sr)) {
while (true) {
synchronized (Thread.currentThread()) {
Thread.currentThread().wait(100);
}
}
// }
// }
// }
} catch (Throwable tr) {
Log.e(TAG, "", tr);
}
}, "i2pd-stdin").start();
return "ok";
} catch (Throwable tr) {
Log.e(TAG, "", tr);
return "Error in exec(): " + tr;
}
}
public static void stopDaemon(Throwable tr){
AbstractProcess p = i2pdProcess;
if (p != null) {
p.kill(tr);
i2pdProcess = null;
}
}
public static void startAcceptingTunnels(){}
public static void stopAcceptingTunnels(){}
public static void reloadTunnelsConfigs(){}
public static int getTransitTunnelsCount(){return -1;}
public static String getWebConsAddr(){return "";}
public static String getDataDir() {
return dataDir;
}
public static boolean getHTTPProxyState(){return false;}
public static boolean getSOCKSProxyState(){return false;}
public static boolean getBOBState(){return false;}
public static boolean getSAMState(){return false;}
public static boolean getI2CPState(){return false;}
public static void onNetworkStateChanged(boolean isConnected){}
}

2
app/src/main/java/org/purplei2p/i2pd/NetworkStateChangeReceiver.java

@ -19,7 +19,7 @@ public class NetworkStateChangeReceiver extends BroadcastReceiver {
NetworkInfo activeNetworkInfo = cm.getActiveNetworkInfo(); NetworkInfo activeNetworkInfo = cm.getActiveNetworkInfo();
boolean isConnected = activeNetworkInfo != null && activeNetworkInfo.isConnected(); boolean isConnected = activeNetworkInfo != null && activeNetworkInfo.isConnected();
//I2PD_JNI.onNetworkStateChanged(isConnected); I2PD_JNI.onNetworkStateChanged(isConnected);
} catch (Throwable tr) { } catch (Throwable tr) {
Log.e(TAG, "", tr); Log.e(TAG, "", tr);
} }

2
app/src/main/java/org/purplei2p/i2pd/WebConsoleActivity.java

@ -27,7 +27,7 @@ public class WebConsoleActivity extends Activity {
final WebSettings webSettings = webView.getSettings(); final WebSettings webSettings = webView.getSettings();
webSettings.setBuiltInZoomControls(true); webSettings.setBuiltInZoomControls(true);
webSettings.setJavaScriptEnabled(false); webSettings.setJavaScriptEnabled(false);
webView.loadUrl("http://localhost:7070/"/*I2PD_JNI.getWebConsAddr()*/); webView.loadUrl(I2PD_JNI.getWebConsAddr());
swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe); swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe);
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {

126
app/src/main/java/org/purplei2p/i2pd/appscope/App.java

@ -1,126 +0,0 @@
package org.purplei2p.i2pd.appscope;
import android.app.Application;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.net.ConnectivityManager;
import android.os.IBinder;
import android.util.Log;
import org.purplei2p.i2pd.BuildConfig;
import org.purplei2p.i2pd.I2PDActivity;
import org.purplei2p.i2pd.*;
public class App extends Application {
private static final String TAG = "i2pd.app";
//private static final I2PD_JNI jniHolder = new I2PD_JNI();
private static volatile DaemonWrapper daemonWrapper;
private String versionName;
private static volatile boolean mIsBound;
public synchronized static DaemonWrapper getDaemonWrapper() {
return daemonWrapper;
}
@Override
public void onCreate() {
super.onCreate();
synchronized (this) {
if (getDaemonWrapper() == null) {
createDaemonWrapper();
}
versionName = BuildConfig.VERSION_NAME;
doBindService();
startService(new Intent(this, ForegroundService.class));
}
}
private void createDaemonWrapper() {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(
Context.CONNECTIVITY_SERVICE);
daemonWrapper = new DaemonWrapper(getApplicationContext(), getAssets(), connectivityManager);
ForegroundService.init(daemonWrapper);
}
private synchronized void doBindService() {
if (mIsBound)
return;
// Establish a connection with the service. We use an explicit
// class name because we want a specific service implementation that
// we know will be running in our own process (and thus won't be
// supporting component replacement by other applications).
bindService(new Intent(this, ForegroundService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
private synchronized void doUnbindService() {
if (mIsBound) {
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
}
}
@Override
public void onTerminate() {
quit();
super.onTerminate();
}
private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
/* This is called when the connection with the service has been
established, giving us the service object we can use to
interact with the service. Because we have bound to a explicit
service that we know is running in our own process, we can
cast its IBinder to a concrete class and directly access it. */
// mBoundService = ((LocalService.LocalBinder)service).getService();
/* Tell the user about this for our demo. */
// Toast.makeText(Binding.this, R.string.local_service_connected,
// Toast.LENGTH_SHORT).show();
}
public void onServiceDisconnected(ComponentName className) {
/* This is called when the connection with the service has been
unexpectedly disconnected -- that is, its process crashed.
Because it is running in our same process, we should never
see this happen. */
// mBoundService = null;
// Toast.makeText(Binding.this, R.string.local_service_disconnected,
// Toast.LENGTH_SHORT).show();
}
};
public synchronized void quit() {
try {
if(daemonWrapper!=null)daemonWrapper.stopDaemon(null);
} catch (Throwable tr) {
Log.e(TAG, "", tr);
}
try {
doUnbindService();
} catch (IllegalArgumentException ex) {
Log.e(TAG, "throwable caught and ignored", ex);
if (ex.getMessage().startsWith("Service not registered: " + I2PDActivity.class.getName())) {
Log.i(TAG, "Service not registered exception seems to be normal, not a bug it seems.");
}
} catch (Throwable tr) {
Log.e(TAG, "throwable caught and ignored", tr);
}
try{
ForegroundService fs = ForegroundService.getInstance();
if(fs!=null)fs.stop();
}catch(Throwable tr) {
Log.e(TAG, "", tr);
}
}
}

4
app/src/main/res/layout/activity_main.xml

@ -58,7 +58,7 @@
<Space <Space
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/margin_medium" /> android:layout_height="@dimen/margin_medium" />
<!--
<TextView <TextView
android:id="@+id/textView" android:id="@+id/textView"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -149,7 +149,7 @@
android:textColor="#DFDFDF" /> android:textColor="#DFDFDF" />
</TableRow> </TableRow>
</TableLayout> </TableLayout>
-->
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>

4
app/src/main/res/menu/options_main.xml

@ -15,10 +15,10 @@
android:id="@+id/action_start_webview" android:id="@+id/action_start_webview"
android:orderInCategory="96" android:orderInCategory="96"
android:title="@string/action_start_webview" /> android:title="@string/action_start_webview" />
<!--<item <item
android:id="@+id/action_reload_tunnels_config" android:id="@+id/action_reload_tunnels_config"
android:orderInCategory="97" android:orderInCategory="97"
android:title="@string/action_reload_tunnels_config" />--> android:title="@string/action_reload_tunnels_config" />
<item <item
android:id="@+id/action_graceful_stop" android:id="@+id/action_graceful_stop"
android:orderInCategory="98" android:orderInCategory="98"

14
binary/jni/build.sh

@ -27,7 +27,7 @@ help()
{ {
echo "Syntax: $(basename "$SOURCE") [-m|d|s|h|v]" echo "Syntax: $(basename "$SOURCE") [-m|d|s|h|v]"
echo "Options:" echo "Options:"
echo "m Rename binaries as libraries." echo "b Build binary."
echo "d Debug build." echo "d Debug build."
echo "s Strip binaries." echo "s Strip binaries."
echo "x Skip libraries rebuild." echo "x Skip libraries rebuild."
@ -36,13 +36,13 @@ help()
echo echo
} }
while getopts ":dmsvxh" option; do while getopts ":dbsvxh" option; do
case $option in case $option in
d) # debug build d) # debug build
_NDK_OPTS="$_NDK_OPTS NDK_DEBUG=1" _NDK_OPTS="$_NDK_OPTS NDK_DEBUG=1"
;; ;;
m) # make module b) # build binary
_MODULE=1 _BINARY=1
;; ;;
s) # strip binaries s) # strip binaries
_STRIP=1 _STRIP=1
@ -74,8 +74,10 @@ if [ -z "$_SKIP_LIBS" ]; then
./build_miniupnpc.sh ./build_miniupnpc.sh
fi fi
echo "Building i2pd..." if [ ! -z "$_BINARY" ]; then
$ANDROID_NDK_HOME/ndk-build $_NDK_OPTS echo "Building i2pd..."
$ANDROID_NDK_HOME/ndk-build $_NDK_OPTS
fi
echo "Processing binaries (if requested)..." echo "Processing binaries (if requested)..."
pushd $DIR/../libs > /dev/null pushd $DIR/../libs > /dev/null

0
binary/libs/.gitkeep

2
fastlane/metadata/android/en-US/changelogs/2530010.txt

@ -0,0 +1,2 @@
* Updated codebase to 2.53.1
* Revert to usage of the JNI
Loading…
Cancel
Save