Browse Source
* Fix foreground notification * Trying to fix app quit * Remove unsupported UI items * Remove misleading exceptions * Set Unix exec bit on build scripts * Revamp processAssets point to be launched * Corrected logic of daemon status events * Fix double I2PActivity.onCreate call hangup * Fix exception processing @ processAssetspull/82/head
nonlin-lin-chaos-order-etc-etal
4 months ago
committed by
GitHub
18 changed files with 505 additions and 156 deletions
@ -0,0 +1,7 @@ |
|||||||
|
package org.purplei2p.i2pd; |
||||||
|
|
||||||
|
public interface AbstractProcess { |
||||||
|
/** @param tr can be null |
||||||
|
*/ |
||||||
|
void kill(Throwable tr); |
||||||
|
} |
@ -1,35 +0,0 @@ |
|||||||
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); |
|
||||||
} |
|
@ -0,0 +1,154 @@ |
|||||||
|
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){} |
||||||
|
} |
@ -0,0 +1,126 @@ |
|||||||
|
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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
export I2PD_DEBUG=1 |
||||||
|
if [ -z "$ANDROID_NDK_HOME" -a "$ANDROID_NDK_HOME" == "" ]; then |
||||||
|
echo -e "\033[31mFailed! ANDROID_NDK_HOME is empty. Run 'export ANDROID_NDK_HOME=[PATH_TO_NDK]'\033[0m" |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
echo Building boost... |
||||||
|
./build_boost.sh |
||||||
|
echo Building miniupnpc... |
||||||
|
./build_miniupnpc.sh |
||||||
|
echo Building openssl... |
||||||
|
./build_openssl.sh |
||||||
|
echo Building i2pd... |
||||||
|
pushd . |
||||||
|
NDK_MODULE_PATH=`pwd` |
||||||
|
cd .. |
||||||
|
NDK_PROJECT_PATH=`pwd` |
||||||
|
popd |
||||||
|
if [ -z "$BUILD_SO" -a "$BUILD_SO" == "" ]; then |
||||||
|
export NDK_MODULE_PATH=$NDK_MODULE_PATH && export NDK_PROJECT_PATH=$NDK_PROJECT_PATH && $ANDROID_NDK_HOME/ndk-build V=1 NDK_LOG=1 -j`nproc` NDK_DEBUG=1 |
||||||
|
else |
||||||
|
export NDK_MODULE_PATH=$NDK_MODULE_PATH && export NDK_PROJECT_PATH=$NDK_PROJECT_PATH && ./ndkbuild-wrapper.sh V=1 NDK_LOG=1 -j`nproc` NDK_DEBUG=1 |
||||||
|
fi |
||||||
|
echo "$0 completed." |
@ -0,0 +1,20 @@ |
|||||||
|
if [ -z "$ANDROID_NDK_HOME" -a "$ANDROID_NDK_HOME" == "" ]; then |
||||||
|
echo -e "\033[31mFailed! ANDROID_NDK_HOME is empty. Run 'export ANDROID_NDK_HOME=[PATH_TO_NDK]'\033[0m" |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
echo Building boost... |
||||||
|
./build_boost.sh |
||||||
|
echo Building miniupnpc... |
||||||
|
./build_miniupnpc.sh |
||||||
|
echo Building openssl... |
||||||
|
./build_openssl.sh |
||||||
|
echo Building i2pd... |
||||||
|
NDK_MODULE_PATH=`pwd` |
||||||
|
cd .. |
||||||
|
NDK_PROJECT_PATH=`pwd` |
||||||
|
if [ -z "$BUILD_SO" -a "$BUILD_SO" == "" ]; then |
||||||
|
export NDK_MODULE_PATH=$NDK_MODULE_PATH && export NDK_PROJECT_PATH=$NDK_PROJECT_PATH && $ANDROID_NDK_HOME/ndk-build V=1 NDK_LOG=1 -j`nproc` |
||||||
|
else |
||||||
|
export NDK_MODULE_PATH=$NDK_MODULE_PATH && export NDK_PROJECT_PATH=$NDK_PROJECT_PATH && ./ndkbuild-wrapper.sh V=1 NDK_LOG=1 -j`nproc` |
||||||
|
fi |
||||||
|
echo "$0 completed." |
@ -0,0 +1,16 @@ |
|||||||
|
#!/usr/bin/env bash |
||||||
|
|
||||||
|
cd $NDK_PROJECT_PATH/jni |
||||||
|
$ANDROID_NDK_HOME/ndk-build $* |
||||||
|
|
||||||
|
# if it doesn't exist, then create |
||||||
|
if [ ! -d $NDK_PROJECT_PATH/libs ]; then mkdir $NDK_PROJECT_PATH/libs; fi |
||||||
|
|
||||||
|
cd $NDK_PROJECT_PATH/libs |
||||||
|
for f in $(ls .); |
||||||
|
do |
||||||
|
if [ -z "$I2PD_DEBUG" -a "$I2PD_DEBUG" == "" ]; then |
||||||
|
strip $f/i2pd |
||||||
|
fi |
||||||
|
mv $f/i2pd $f/libi2pd.so |
||||||
|
done |
@ -1,3 +1,4 @@ |
|||||||
android.enableJetifier=true |
android.enableJetifier=true |
||||||
android.useAndroidX=true |
android.useAndroidX=true |
||||||
org.gradle.parallel=true |
org.gradle.parallel=true |
||||||
|
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 |
||||||
|
Loading…
Reference in new issue