diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
index 58d46ed8..b6cc6f26 100755
--- a/android/AndroidManifest.xml
+++ b/android/AndroidManifest.xml
@@ -3,14 +3,23 @@
package="org.purplei2p.i2pd"
android:versionCode="1"
android:versionName="1.0">
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/jni/Android.mk b/android/jni/Android.mk
index bef6d5ec..90a679b2 100755
--- a/android/jni/Android.mk
+++ b/android/jni/Android.mk
@@ -12,7 +12,6 @@ LOCAL_STATIC_LIBRARIES := \
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 \
@@ -58,9 +57,8 @@ LOCAL_SRC_FILES := DaemonAndroid.cpp i2pd_android.cpp \
../../TunnelEndpoint.cpp \
../../TunnelGateway.cpp \
../../TunnelPool.cpp \
- ../../UPnP.cpp \
../../util.cpp \
- ../../i2pd.cpp
+ ../../i2pd.cpp ../../UPnP.cpp
include $(BUILD_SHARED_LIBRARY)
diff --git a/android/jni/Application.mk b/android/jni/Application.mk
index 34f9dd63..e8a51add 100755
--- a/android/jni/Application.mk
+++ b/android/jni/Application.mk
@@ -1,5 +1,7 @@
#APP_ABI := all
-APP_ABI := armeabi-v7a x86
+#APP_ABI := armeabi-v7a x86
+#APP_ABI := x86
+APP_ABI := armeabi-v7a
#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
@@ -11,7 +13,7 @@ APP_STL := gnustl_shared
# Enable c++11 extentions in source code
APP_CPPFLAGS += -std=c++11
-APP_CPPFLAGS += -DUSE_UPNP -DANDROID -D__ANDROID__
+APP_CPPFLAGS += -DANDROID -D__ANDROID__ -DUSE_UPNP
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
APP_CPPFLAGS += -DANDROID_ARM7A
endif
diff --git a/android/jni/DaemonAndroid.cpp b/android/jni/DaemonAndroid.cpp
index 02f6e3f7..038a07fa 100644
--- a/android/jni/DaemonAndroid.cpp
+++ b/android/jni/DaemonAndroid.cpp
@@ -1,5 +1,9 @@
#include "DaemonAndroid.h"
#include "../../Daemon.h"
+#include
+#include
+#include
+#include
//#include "mainwindow.h"
namespace i2p
@@ -58,10 +62,11 @@ namespace android
}
}
*/
- DaemonAndroidImpl::DaemonAndroidImpl ():
+ DaemonAndroidImpl::DaemonAndroidImpl ()
+ //:
/*mutex(nullptr), */
- m_IsRunning(false),
- m_RunningChangedCallback(nullptr)
+ //m_IsRunning(false),
+ //m_RunningChangedCallback(nullptr)
{
}
@@ -73,15 +78,15 @@ namespace android
bool DaemonAndroidImpl::init(int argc, char* argv[])
{
//mutex=new QMutex(QMutex::Recursive);
- setRunningCallback(0);
- m_IsRunning=false;
+ //setRunningCallback(0);
+ //m_IsRunning=false;
return Daemon.init(argc,argv);
}
void DaemonAndroidImpl::start()
{
//QMutexLocker locker(mutex);
- setRunning(true);
+ //setRunning(true);
Daemon.start();
}
@@ -89,7 +94,7 @@ namespace android
{
//QMutexLocker locker(mutex);
Daemon.stop();
- setRunning(false);
+ //setRunning(false);
}
void DaemonAndroidImpl::restart()
@@ -98,7 +103,7 @@ namespace android
stop();
start();
}
-
+ /*
void DaemonAndroidImpl::setRunningCallback(runningChangedCallback cb)
{
m_RunningChangedCallback = cb;
@@ -119,46 +124,65 @@ namespace android
m_RunningChangedCallback();
}
}
-
+*/
static DaemonAndroidImpl daemon;
-
+ static char* argv[1]={strdup("tmp")};
/**
- * returns 1 if daemon init failed
- * returns 0 if daemon initialized and started okay
+ * returns error details if failed
+ * returns "ok" if daemon initialized and started okay
*/
- int start(/*int argc, char* argv[]*/)
+ std::string start(/*int argc, char* argv[]*/)
{
- int result;
-
+ try
{
- //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 ();
+ //int result;
{
- //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;
+ //Log.d(TAG"Initialising the daemon...");
+ bool daemonInitSuccess = daemon.init(1,argv);
+ if(!daemonInitSuccess)
+ {
+ //QMessageBox::critical(0, "Error", "Daemon init failed");
+ return "Daemon init failed";
+ }
+ //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();
+ }
}
- }
- //QMessageBox::information(&w, "Debug", "demon stopped");
- //Log.d(TAG"Exiting the application");
- //return result;
+ //QMessageBox::information(&w, "Debug", "demon stopped");
+ //Log.d(TAG"Exiting the application");
+ //return result;
+ }
+ 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()
diff --git a/android/jni/DaemonAndroid.h b/android/jni/DaemonAndroid.h
index f6ee618f..9cc8219b 100644
--- a/android/jni/DaemonAndroid.h
+++ b/android/jni/DaemonAndroid.h
@@ -1,11 +1,12 @@
#ifndef DAEMON_ANDROID_H
#define DAEMON_ANDROID_H
+#include
+
namespace i2p
{
namespace android
{
- //FIXME currently NOT threadsafe
class DaemonAndroidImpl
{
public:
@@ -13,7 +14,7 @@ namespace android
DaemonAndroidImpl ();
~DaemonAndroidImpl ();
- typedef void (*runningChangedCallback)();
+ //typedef void (*runningChangedCallback)();
/**
* @return success
@@ -22,21 +23,21 @@ namespace android
void start();
void stop();
void restart();
- void setRunningCallback(runningChangedCallback cb);
- bool isRunning();
+ //void setRunningCallback(runningChangedCallback cb);
+ //bool isRunning();
private:
- void setRunning(bool running);
+ //void setRunning(bool running);
private:
//QMutex* mutex;
- bool m_IsRunning;
- runningChangedCallback m_RunningChangedCallback;
+ //bool m_IsRunning;
+ //runningChangedCallback m_RunningChangedCallback;
};
/**
- * returns 1 if daemon init failed
- * returns 0 if daemon initialized and started okay
+ * returns "ok" if daemon init failed
+ * returns errinfo if daemon initialized and started okay
*/
- int start();
+ std::string start();
// stops the daemon
void stop();
diff --git a/android/jni/i2pd_android.cpp b/android/jni/i2pd_android.cpp
index b84ec1ac..40b50345 100755
--- a/android/jni/i2pd_android.cpp
+++ b/android/jni/i2pd_android.cpp
@@ -3,6 +3,7 @@
#include
#include "org_purplei2p_i2pd_I2PD_JNI.h"
#include "DaemonAndroid.h"
+#include "../../RouterContext.h"
JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getABICompiledWith
(JNIEnv * env, jclass clazz) {
@@ -41,9 +42,9 @@ JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_getABICompiledWith
return env->NewStringUTF(ABI);
}
-JNIEXPORT jint JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startDaemon
+JNIEXPORT jstring JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_startDaemon
(JNIEnv * env, jclass clazz) {
- return (jint)i2p::android::start();
+ return env->NewStringUTF(i2p::android::start().c_str());
}
JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopDaemon
@@ -51,3 +52,12 @@ JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_stopDaemon
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_onNetworkStateChanged
+ (JNIEnv * env, jclass clazz, jboolean isConnected) {
+ bool isConnectedBool = (bool) isConnected;
+}
diff --git a/android/jni/org_purplei2p_i2pd_I2PD_JNI.h b/android/jni/org_purplei2p_i2pd_I2PD_JNI.h
index ddbcace8..04923d22 100644
--- a/android/jni/org_purplei2p_i2pd_I2PD_JNI.h
+++ b/android/jni/org_purplei2p_i2pd_I2PD_JNI.h
@@ -15,6 +15,18 @@ extern "C" {
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_stopAcceptingTunnels
+ (JNIEnv *, jclass);
+
+JNIEXPORT void JNICALL Java_org_purplei2p_i2pd_I2PD_1JNI_onNetworkStateChanged
+ (JNIEnv * env, jclass clazz, jboolean isConnected);
+
#ifdef __cplusplus
}
#endif
diff --git a/android/project.properties b/android/project.properties
index c6998b3d..7ce68660 100644
--- a/android/project.properties
+++ b/android/project.properties
@@ -11,4 +11,4 @@
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
-target=android-9
+target=android-24
diff --git a/android/res/menu/options_main.xml b/android/res/menu/options_main.xml
new file mode 100644
index 00000000..388dfd83
--- /dev/null
+++ b/android/res/menu/options_main.xml
@@ -0,0 +1,16 @@
+
diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml
index b02181c8..8c78e88b 100755
--- a/android/res/values/strings.xml
+++ b/android/res/values/strings.xml
@@ -1,4 +1,9 @@
i2pd
+ i2pd started
+ Quit
+ Graceful Quit
+ Graceful quit is already in progress
+ Graceful quit is in progress
diff --git a/android/src/org/purplei2p/i2pd/ForegroundService.java b/android/src/org/purplei2p/i2pd/ForegroundService.java
new file mode 100644
index 00000000..6ff826c4
--- /dev/null
+++ b/android/src/org/purplei2p/i2pd/ForegroundService.java
@@ -0,0 +1,88 @@
+package org.purplei2p.i2pd;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.support.v4.app.NotificationCompat;
+import android.util.Log;
+
+public class ForegroundService extends Service {
+// private NotificationManager mNM;
+
+ // Unique Identification Number for the Notification.
+ // We use it on Notification start, and to cancel it.
+ private int NOTIFICATION = R.string.i2pd_started;
+
+ /**
+ * Class for clients to access. Because we know this service always
+ * runs in the same process as its clients, we don't need to deal with
+ * IPC.
+ */
+ public class LocalBinder extends Binder {
+ ForegroundService getService() {
+ return ForegroundService.this;
+ }
+ }
+
+ @Override
+ public void onCreate() {
+// mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
+
+ // Display a notification about us starting. We put an icon in the status bar.
+ showNotification();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.i("ForegroundService", "Received start id " + startId + ": " + intent);
+ return START_NOT_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ // Cancel the persistent notification.
+ //mNM.cancel(NOTIFICATION);
+ stopForeground(true);
+
+ // Tell the user we stopped.
+ //Toast.makeText(this, R.string.local_service_stopped, Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ // This is the object that receives interactions from clients. See
+ // RemoteService for a more complete example.
+ private final IBinder mBinder = new LocalBinder();
+
+ /**
+ * Show a notification while this service is running.
+ */
+ private void showNotification() {
+ // In this sample, we'll use the same text for the ticker and the expanded notification
+ CharSequence text = getText(R.string.i2pd_started);
+
+ // The PendingIntent to launch our activity if the user selects this notification
+ PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
+ new Intent(this, I2PD.class), 0);
+
+ // Set the info for the views that show in the notification panel.
+ Notification notification = new NotificationCompat.Builder(this)
+ .setSmallIcon(R.drawable.itoopie_notification_icon) // the status icon
+ .setTicker(text) // the status text
+ .setWhen(System.currentTimeMillis()) // the time stamp
+ .setContentTitle(getText(R.string.app_name)) // the label of the entry
+ .setContentText(text) // the contents of the entry
+ .setContentIntent(contentIntent) // The intent to send when the entry is clicked
+ .build();
+
+ // Send the notification.
+ //mNM.notify(NOTIFICATION, notification);
+ startForeground(NOTIFICATION, notification);
+ }
+}
\ No newline at end of file
diff --git a/android/src/org/purplei2p/i2pd/I2PD.java b/android/src/org/purplei2p/i2pd/I2PD.java
index 6f77c53f..ef22f941 100755
--- a/android/src/org/purplei2p/i2pd/I2PD.java
+++ b/android/src/org/purplei2p/i2pd/I2PD.java
@@ -1,20 +1,272 @@
package org.purplei2p.i2pd;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import android.annotation.SuppressLint;
import android.app.Activity;
-import android.widget.TextView;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Build;
import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.TextView;
+import android.widget.Toast;
public class I2PD extends Activity {
+ private static final String TAG = "i2pd";
+
+ private static Throwable loadLibsThrowable;
+ static {
+ try {
+ I2PD_JNI.loadLibraries();
+ } catch (Throwable tr) {
+ loadLibsThrowable = tr;
+ }
+ }
+ private String daemonStartResult="N/A";
+ private boolean destroyed=false;
+ private TextView textView;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ destroyed=false;
+
+ //set the app be foreground (do not unload when RAM needed)
+ doBindService();
- TextView tv = new TextView(this);
- tv.setText( "libi2pd.so was compiled with ABI " + getABICompiledWith());
- setContentView(tv);
+ textView = new TextView(this);
+ setContentView(textView);
+ if (loadLibsThrowable != null) {
+ textView.setText(throwableToString(loadLibsThrowable)+"\r\n");
+ return;
+ }
+ try {
+ textView.setText(
+ "libi2pd.so was compiled with ABI " + getABICompiledWith() + "\r\n"+
+ "Starting daemon... ");
+ new Thread(new Runnable(){
+
+ @Override
+ public void run() {
+ try {
+ doStartDaemon();
+ } catch (final Throwable tr) {
+ appendThrowable(tr);
+ }
+ }
+
+ },"i2pdDaemonStarting").start();
+ } catch (Throwable tr) {
+ textView.setText(textView.getText().toString()+throwableToString(tr));
+ }
}
- public String getABICompiledWith() {
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ localDestroy();
+ }
+
+ private synchronized void localDestroy() {
+ if(destroyed)return;
+ destroyed=true;
+ if(gracefulQuitTimer!=null) {
+ gracefulQuitTimer.cancel();
+ gracefulQuitTimer = null;
+ }
+ try{
+ doUnbindService();
+ }catch(Throwable tr){
+ Log.e(TAG, "", tr);
+ }
+ if("ok".equals(daemonStartResult)) {
+ try {
+ I2PD_JNI.stopDaemon();
+ } catch (Throwable tr) {
+ Log.e(TAG, "error", tr);
+ }
+ }
+ }
+
+ private CharSequence throwableToString(Throwable tr) {
+ StringWriter sw = new StringWriter(8192);
+ PrintWriter pw = new PrintWriter(sw);
+ tr.printStackTrace(pw);
+ pw.close();
+ return sw.toString();
+ }
+
+ public String getABICompiledWith() {
return I2PD_JNI.getABICompiledWith();
}
+
+ private synchronized void doStartDaemon() {
+ if(destroyed)return;
+ daemonStartResult = I2PD_JNI.startDaemon();
+ runOnUiThread(new Runnable(){
+
+ @Override
+ public void run() {
+ synchronized (I2PD.this) {
+ if(destroyed)return;
+ textView.setText(
+ textView.getText().toString()+
+ "start result: "+daemonStartResult+"\r\n"
+ );
+ }
+ }
+
+ });
+ }
+
+ private void appendThrowable(final Throwable tr) {
+ runOnUiThread(new Runnable(){
+
+ @Override
+ public void run() {
+ synchronized (I2PD.this) {
+ if(destroyed)return;
+ textView.setText(textView.getText().toString()+throwableToString(tr)+"\r\n");
+ }
+ }
+ });
+ }
+
+// private LocalService mBoundService;
+
+ private 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();
+ }
+ };
+
+
+ private boolean mIsBound;
+
+ private void doBindService() {
+ // 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 void doUnbindService() {
+ if (mIsBound) {
+ // Detach our existing connection.
+ unbindService(mConnection);
+ mIsBound = false;
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.options_main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+
+ switch(id){
+ case R.id.action_quit:
+ quit();
+ return true;
+ case R.id.action_graceful_quit:
+ gracefulQuit();
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ @SuppressLint("NewApi")
+ private void quit() {
+ try {
+ localDestroy();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ finishAndRemoveTask();
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ finishAffinity();
+ } else {
+ //moveTaskToBack(true);
+ finish();
+ }
+ }catch (Throwable tr) {
+ Log.e(TAG, "", tr);
+ }
+ }
+
+ private Timer gracefulQuitTimer;
+ private synchronized void gracefulQuit() {
+ if(gracefulQuitTimer!=null){
+ Toast.makeText(this, R.string.graceful_quit_is_already_in_progress,
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ Toast.makeText(this, R.string.graceful_quit_is_in_progress,
+ Toast.LENGTH_SHORT).show();
+ new Thread(new Runnable(){
+
+ @Override
+ public void run() {
+ try{
+ synchronized (I2PD.this) {
+ if("ok".equals(daemonStartResult)) {
+ I2PD_JNI.stopAcceptingTunnels();
+ gracefulQuitTimer = new Timer(true);
+ gracefulQuitTimer.schedule(new TimerTask(){
+
+ @Override
+ public void run() {
+ quit();
+ }
+
+ }, 10*60*1000/*millis*/);
+ }else{
+ quit();
+ }
+ }
+ } catch(Throwable tr) {
+ Log.e(TAG,"",tr);
+ }
+ }
+
+ },"gracQuitInit").start();
+ }
}
diff --git a/android/src/org/purplei2p/i2pd/I2PD_JNI.java b/android/src/org/purplei2p/i2pd/I2PD_JNI.java
index 040cca1c..4f5913e3 100644
--- a/android/src/org/purplei2p/i2pd/I2PD_JNI.java
+++ b/android/src/org/purplei2p/i2pd/I2PD_JNI.java
@@ -3,14 +3,19 @@ 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
+ * returns error info if failed
+ * returns "ok" if daemon initialized and started okay
*/
- public static native int startDaemon();
+ public static native String startDaemon();
//should only be called after startDaemon() success
public static native void stopDaemon();
+
+ public static native void stopAcceptingTunnels();
+
+ public static native void onNetworkStateChanged(boolean isConnected);
- static {
+ public static void loadLibraries() {
+ //System.loadLibrary("gnustl_shared");
System.loadLibrary("i2pd");
}
}
diff --git a/android/src/org/purplei2p/i2pd/NetworkStateChangeReceiver.java b/android/src/org/purplei2p/i2pd/NetworkStateChangeReceiver.java
new file mode 100644
index 00000000..e2f284b0
--- /dev/null
+++ b/android/src/org/purplei2p/i2pd/NetworkStateChangeReceiver.java
@@ -0,0 +1,30 @@
+package org.purplei2p.i2pd;
+
+import android.util.Log;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+
+public class NetworkStateChangeReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "i2pd";
+
+ //api level 1
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ Log.d(TAG,"Network state change");
+ try {
+ ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo activeNetworkInfo = cm.getActiveNetworkInfo();
+ boolean isConnected = activeNetworkInfo!=null && activeNetworkInfo.isConnected();
+ // https://developer.android.com/training/monitoring-device-state/connectivity-monitoring.html?hl=ru
+ // boolean isWiFi = activeNetworkInfo!=null && (activeNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI);
+
+ I2PD_JNI.onNetworkStateChanged(isConnected);
+ } catch (Throwable tr) {
+ Log.d(TAG,"",tr);
+ }
+ }
+}