diff --git a/.gitmodules b/.gitmodules index 2f69bb7b2..5199bdb4d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -17,3 +17,6 @@ [submodule "supportlibs/libsam3"] path = supportlibs/libsam3 url = https://github.com/i2p/libsam3.git +[submodule "supportlibs/jni.hpp"] + path = supportlibs/jni.hpp + url = https://github.com/RetroShare/jni.hpp.git diff --git a/build_scripts/Android/prepare-toolchain-clang.sh b/build_scripts/Android/prepare-toolchain-clang.sh index 25fefccba..510b04855 100755 --- a/build_scripts/Android/prepare-toolchain-clang.sh +++ b/build_scripts/Android/prepare-toolchain-clang.sh @@ -112,6 +112,9 @@ define_default_value MVPTREE_SOURCE_VERSION origin/master define_default_value REPORT_DIR "$(pwd)/$(basename ${NATIVE_LIBS_TOOLCHAIN_PATH})_build_report/" +define_default_value RS_SRC_DIR "$(realpath $(dirname $BASH_SOURCE)/../../)" + + cArch="" eABI="" cmakeABI="" @@ -829,6 +832,15 @@ build_phash() popd } +task_register fetch_jni_hpp +fetch_jni_hpp() +{ + local rDir="supportlibs/jni.hpp/" + + [ "$(ls "${RS_SRC_DIR}/${rDir}" | wc -l)" -gt "4" ] || + git -C ${RS_SRC_DIR} submodule update --init ${rDir} +} + task_register build_mvptree build_mvptree() { @@ -862,6 +874,7 @@ build_default_toolchain() task_run build_xapian || return $? task_run build_miniupnpc || return $? task_run build_phash || return $? + task_run fetch_jni_hpp || return $? task_run deduplicate_includes || return $? task_run get_native_libs_toolchain_path || return $? } diff --git a/libretroshare/src/jsonapi/jsonapi.cpp b/libretroshare/src/jsonapi/jsonapi.cpp index 4b003f225..2f2ded30c 100644 --- a/libretroshare/src/jsonapi/jsonapi.cpp +++ b/libretroshare/src/jsonapi/jsonapi.cpp @@ -796,8 +796,9 @@ void JsonApiServer::run() } catch(std::exception& e) { - RsErr() << __PRETTY_FUNCTION__ << " Failure starting JSON API server: " - << e.what() << std::endl; + /* TODO: find a way to report back programmatically if failed listening + * port */ + RS_ERR("Failure starting JSON API server: ", e.what()); print_stacktrace(); return; } diff --git a/libretroshare/src/libretroshare.pro b/libretroshare/src/libretroshare.pro index 36103f856..2bad4682f 100644 --- a/libretroshare/src/libretroshare.pro +++ b/libretroshare/src/libretroshare.pro @@ -1101,13 +1101,11 @@ android-* { DEFINES *= "fseeko64=fseeko" DEFINES *= "ftello64=ftello" - ## @See: android_ifaddrs/README.adoc - !contains(DEFINES, LIBRETROSHARE_ANDROID_IFADDRS_QT) { - HEADERS += \ - android_ifaddrs/ifaddrs-android.h \ - android_ifaddrs/LocalArray.h \ - android_ifaddrs/ScopedFd.h - } +## @See: rs_android/README-ifaddrs-android.adoc + HEADERS += \ + rs_android/ifaddrs-android.h \ + rs_android/LocalArray.h \ + rs_android/ScopedFd.h } ## Static library are very susceptible to order in command line @@ -1116,6 +1114,13 @@ android-* { LIBS += $$linkStaticLibs(sLibs) PRE_TARGETDEPS += $$pretargetStaticLibs(sLibs) - HEADERS += util/androiddebug.h + HEADERS += \ + rs_android/androidcoutcerrcatcher.hpp \ + rs_android/retroshareserviceandroid.hpp \ + rs_android/rsjni.hpp + + SOURCES += rs_android/rsjni.cpp \ + rs_android/retroshareserviceandroid.cpp \ + rs_android/errorconditionwrap.cpp } diff --git a/libretroshare/src/pqi/p3peermgr.cc b/libretroshare/src/pqi/p3peermgr.cc index 83c4d9366..f1d604ea5 100644 --- a/libretroshare/src/pqi/p3peermgr.cc +++ b/libretroshare/src/pqi/p3peermgr.cc @@ -1398,11 +1398,11 @@ bool p3PeerMgrIMPL::UpdateOwnAddress( const sockaddr_storage& pLocalAddr, sockaddr_storage_copy(pExtAddr, extAddr); sockaddr_storage_ipv6_to_ipv4(extAddr); -//#ifdef PEER_DEBUG +#ifdef PEER_DEBUG std::cerr << "p3PeerMgrIMPL::UpdateOwnAddress(" << sockaddr_storage_tostring(localAddr) << ", " << sockaddr_storage_tostring(extAddr) << ")" << std::endl; -//#endif +#endif if( rsBanList && !rsBanList->isAddressAccepted(localAddr, diff --git a/libretroshare/src/pqi/pqinetwork.cc b/libretroshare/src/pqi/pqinetwork.cc index b6c52fc27..2b786f488 100644 --- a/libretroshare/src/pqi/pqinetwork.cc +++ b/libretroshare/src/pqi/pqinetwork.cc @@ -21,16 +21,6 @@ * along with this program. If not, see . * * * *******************************************************************************/ -#ifdef WINDOWS_SYS -# include "util/rswin.h" -# include "util/rsmemory.h" -# include -#endif // WINDOWS_SYS - -/// @See: android_ifaddrs/README.adoc -#ifdef __ANDROID__ -# include -#endif // def __ANDROID__ #include #include @@ -44,6 +34,28 @@ #include "util/rsnet.h" #include "util/stacktrace.h" +#ifdef WINDOWS_SYS +# include "util/rswin.h" +# include "util/rsmemory.h" +# include +#endif // WINDOWS_SYS + +/// @See: android_ifaddrs/README.adoc +#ifdef __ANDROID__ +# include +#endif // def __ANDROID__ + +#ifdef WINDOWS_SYS /* Windows - define errno */ +int errno; +#else /* Windows - define errno */ +#include +#endif + +#ifdef __HAIKU__ +# include +# define IFF_RUNNING 0x0001 +#endif + static struct RsLog::logInfo pqinetzoneInfo = {RsLog::Default, "pqinet"}; #define pqinetzone &pqinetzoneInfo @@ -51,21 +63,6 @@ static struct RsLog::logInfo pqinetzoneInfo = {RsLog::Default, "pqinet"}; * #define NET_DEBUG 1 ****/ -#ifdef WINDOWS_SYS /* Windows - define errno */ - -int errno; - -#else /* Windows - define errno */ - -#include - -#endif - -#ifdef __HAIKU__ - #include - #define IFF_RUNNING 0x0001 -#endif - /********************************** WINDOWS/UNIX SPECIFIC PART ******************/ #ifndef WINDOWS_SYS @@ -271,24 +268,16 @@ int inet_aton(const char *name, struct in_addr *addr) #endif /********************************** WINDOWS/UNIX SPECIFIC PART ******************/ - +#include "util/cxx17retrocompat.h" #include #ifdef WINDOWS_SYS # include # include # pragma comment(lib, "IPHLPAPI.lib") -#elif defined(__ANDROID__) && __ANDROID_API__ < 24 && \ - defined(LIBRETROSHARE_ANDROID_IFADDRS_QT) +#elif defined(__ANDROID__) && __ANDROID_API__ < 24 /// @See: android_ifaddrs/README.adoc -# include -# include -# include -# include -#elif defined(__ANDROID__) && __ANDROID_API__ < 24 && \ - !defined(LIBRETROSHARE_ANDROID_IFADDRS_QT) -/// @See: android_ifaddrs/README.adoc -# include "android_ifaddrs/ifaddrs-android.h" -#else // not __ANDROID__ nor WINDOWS => Linux and other unixes +# include "rs_android/ifaddrs-android.h" +#else // not WINDOWS => Linux and other unixes # include # include #endif // WINDOWS_SYS @@ -331,17 +320,7 @@ bool getLocalAddresses(std::vector& addrs) } } free(adapter_addresses); -#elif defined(__ANDROID__) && __ANDROID_API__ < 24 && \ - defined(LIBRETROSHARE_ANDROID_IFADDRS_QT) -/// @See: android_ifaddrs/README.adoc - for(auto& qAddr: QNetworkInterface::allAddresses()) - { - sockaddr_storage tmpAddr; - sockaddr_storage_clear(tmpAddr); - if(sockaddr_storage_ipv4_aton(tmpAddr, qAddr.toString().toStdString().c_str())) - addrs.push_back(tmpAddr); - } -#else // not WINDOWS_SYS not ANDROID => Linux and other unixes +#else // not WINDOWS_SYS => Linux and other unixes struct ifaddrs *ifsaddrs, *ifa; if(getifaddrs(&ifsaddrs) != 0) { @@ -355,18 +334,19 @@ bool getLocalAddresses(std::vector& addrs) { sockaddr_storage tmp; sockaddr_storage_clear(tmp); - if (sockaddr_storage_copyip(tmp, *reinterpret_cast(ifa->ifa_addr))) + if(sockaddr_storage_copyip( + tmp, + *reinterpret_cast(ifa->ifa_addr) )) addrs.push_back(tmp); } freeifaddrs(ifsaddrs); #endif // WINDOWS_SYS #ifdef NET_DEBUG - std::list::iterator it; - std::cout << "getLocalAddresses(...) returning: <" ; - for(it = addrs.begin(); it != addrs.end(); ++it) - std::cout << sockaddr_storage_iptostring(*it) << ", "; - std::cout << ">" << std::endl; + auto&& dbg = RS_DBG("returning: ["); + for(auto& addr: std::as_const(addrs)) + dbg << sockaddr_storage_iptostring(addr) << ", "; + dbg << "]" << std::endl; #endif return !addrs.empty(); diff --git a/libretroshare/src/pqi/pqinetwork.h b/libretroshare/src/pqi/pqinetwork.h index e4238ee5e..9519bf197 100644 --- a/libretroshare/src/pqi/pqinetwork.h +++ b/libretroshare/src/pqi/pqinetwork.h @@ -4,7 +4,8 @@ * libretroshare: retroshare core library * * * * Copyright (C) 2004-2006 Robert Fernie * - * Copyright (C) 2015-2018 Gioacchino Mazzurco * + * Copyright (C) 2015-2021 Gioacchino Mazzurco * + * Copyright (C) 2021 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -20,8 +21,7 @@ * along with this program. If not, see . * * * *******************************************************************************/ -#ifndef MRK_PQI_NETWORKING_HEADER -#define MRK_PQI_NETWORKING_HEADER +#pragma once #include @@ -86,9 +86,13 @@ extern int errno; /* Define extern errno, to duplicate unix behaviour */ #include #include +#include "util/rsdeprecate.h" + // Same def - different functions... +RS_DEPRECATED_FOR("use std::error_condition instead") void showSocketError(std::string &out); +RS_DEPRECATED_FOR("use std::error_condition instead") std::string socket_errorType(int err); bool getLocalAddresses(std::vector & addrs); @@ -103,10 +107,6 @@ int unix_getsockopt_error(int sockfd, int *err); #ifdef WINDOWS_SYS // WINDOWS /******************* WINDOWS SPECIFIC PART ******************/ +RS_DEPRECATED_FOR("use std::error_condition instead") int WinToUnixError(int error); #endif - - - -#endif - diff --git a/libretroshare/src/android_ifaddrs/LocalArray.h b/libretroshare/src/rs_android/LocalArray.h similarity index 100% rename from libretroshare/src/android_ifaddrs/LocalArray.h rename to libretroshare/src/rs_android/LocalArray.h diff --git a/libretroshare/src/android_ifaddrs/README.adoc b/libretroshare/src/rs_android/README-ifaddrs-android.adoc similarity index 75% rename from libretroshare/src/android_ifaddrs/README.adoc rename to libretroshare/src/rs_android/README-ifaddrs-android.adoc index ba0355df7..0c0065f92 100644 --- a/libretroshare/src/android_ifaddrs/README.adoc +++ b/libretroshare/src/rs_android/README-ifaddrs-android.adoc @@ -24,9 +24,8 @@ plus depending on Qt networking module just for this is frustrating. Update: the warning flood seems have been fixed in later Qt versions https://bugreports.qt.io/browse/QTBUG-86394 -This solution is the first working we implemented in our code it is disabled by -default but can be enabled passing `DEFINES+=LIBRETROSHARE_ANDROID_IFADDRS_QT` -when running `qmake` command. +This solution was the first working we implemented in our code it has been +removed to avoid dependency on Qt, as lighter alternatives are possible. == Code copied from Android Gingerbread release @@ -43,3 +42,8 @@ https://android.googlesource.com/platform/libcore/+/refs/heads/gingerbread-relea is particularly easy to include in our code base and compile. This solution seems the best fitting and doesn't introduce dependency on Qt. +Newer Android releases (expecially 11) have introduced multiple restrictions +on network information access so we suggest you to prepare different APK for +different API level in order to use the `getifaddrs` provided by Android NDK +which deal gracefully with those restrictions as soon as available. + diff --git a/libretroshare/src/android_ifaddrs/ScopedFd.h b/libretroshare/src/rs_android/ScopedFd.h similarity index 100% rename from libretroshare/src/android_ifaddrs/ScopedFd.h rename to libretroshare/src/rs_android/ScopedFd.h diff --git a/libretroshare/src/util/androiddebug.h b/libretroshare/src/rs_android/androidcoutcerrcatcher.hpp similarity index 83% rename from libretroshare/src/util/androiddebug.h rename to libretroshare/src/rs_android/androidcoutcerrcatcher.hpp index c358be553..43cf16dd7 100644 --- a/libretroshare/src/util/androiddebug.h +++ b/libretroshare/src/rs_android/androidcoutcerrcatcher.hpp @@ -1,9 +1,9 @@ /******************************************************************************* - * libretroshare/src/util: androiddebug.h * * * * libretroshare: retroshare core library * * * - * Copyright (C) 2016 Gioacchino Mazzurco * + * Copyright (C) 2016-2021 Gioacchino Mazzurco * + * Copyright (C) 2021 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -36,10 +36,10 @@ * class at the beginning of the main of your program to get them (stdout and * stderr) on logcat output. */ -class AndroidStdIOCatcher +class AndroidCoutCerrCatcher { public: - AndroidStdIOCatcher(const std::string& dTag = "RetroShare", + AndroidCoutCerrCatcher(const std::string& dTag = "RetroShare", android_LogPriority stdout_pri = ANDROID_LOG_INFO, android_LogPriority stderr_pri = ANDROID_LOG_ERROR) : tag(dTag), cout_pri(stdout_pri), cerr_pri(stderr_pri), should_stop(false) @@ -63,10 +63,10 @@ public: pthread_detach(thr); } - ~AndroidStdIOCatcher() + ~AndroidCoutCerrCatcher() { should_stop = true; - pthread_join(thr, NULL); + pthread_join(thr, nullptr); } private: @@ -79,11 +79,13 @@ private: pthread_t thr; std::atomic should_stop; - static void *thread_func(void* instance) + static void* thread_func(void* instance) { - __android_log_write(ANDROID_LOG_INFO, "RetroShare", "Android debugging start"); + __android_log_write( + ANDROID_LOG_INFO, "RetroShare", + "Android standard I/O catcher start" ); - AndroidStdIOCatcher &i = *static_cast(instance); + AndroidCoutCerrCatcher &i = *static_cast(instance); std::string out_buf; std::string err_buf; @@ -113,9 +115,11 @@ private: usleep(10000); } - __android_log_write(ANDROID_LOG_INFO, "RetroShare", "Android debugging stop"); + __android_log_write( + ANDROID_LOG_INFO, "RetroShare", + "Android standard I/O catcher stop" ); - return NULL; + return nullptr; } }; diff --git a/libretroshare/src/rs_android/errorconditionwrap.cpp b/libretroshare/src/rs_android/errorconditionwrap.cpp new file mode 100644 index 000000000..e27495878 --- /dev/null +++ b/libretroshare/src/rs_android/errorconditionwrap.cpp @@ -0,0 +1,42 @@ +/******************************************************************************* + * * + * libretroshare: retroshare core library * + * * + * Copyright (C) 2021 Gioacchino Mazzurco * + * Copyright (C) 2021 Asociación Civil Altermundi * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include "rs_android/rsjni.hpp" + +namespace jni +{ +Local> MakeAnything( + ThingToMake, JNIEnv& env, + const std::error_condition& ec ) +{ + auto& clazz = jni::Class::Singleton(env); + + static auto method = + clazz.GetConstructor(env); + + jni::jint value = ec.value(); + auto message = jni::Make(env, ec.message()); + auto category = jni::Make(env, ec.category().name()); + + return clazz.New(env, method, value, message, category); +} +} diff --git a/libretroshare/src/android_ifaddrs/ifaddrs-android.h b/libretroshare/src/rs_android/ifaddrs-android.h similarity index 100% rename from libretroshare/src/android_ifaddrs/ifaddrs-android.h rename to libretroshare/src/rs_android/ifaddrs-android.h diff --git a/libretroshare/src/rs_android/org/retroshare/service/AssetHelper.java b/libretroshare/src/rs_android/org/retroshare/service/AssetHelper.java new file mode 100644 index 000000000..c32aa5b5d --- /dev/null +++ b/libretroshare/src/rs_android/org/retroshare/service/AssetHelper.java @@ -0,0 +1,98 @@ +/* + * RetroShare + * Copyright (C) 2021 Gioacchino Mazzurco + * Copyright (C) 2021 Asociación Civil Altermundi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-FileCopyrightText: Retroshare Team + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package org.retroshare.service; + +import android.util.Log; +import android.content.Context; +import java.io.OutputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.IOException; + + +public class AssetHelper +{ + public static boolean copyAsset( + Context ctx, String assetPath, String destinationFilePath ) + { + Log.d(TAG, "copyAsset " + assetPath + " -> " + destinationFilePath); + + InputStream in; + OutputStream out; + + try { in = ctx.getAssets().open(assetPath); } + catch(Exception e) + { + Log.e( + TAG, + "Failure opening asset: " + assetPath + " " + e.getMessage() ); + return false; + } + + try { out = new FileOutputStream(destinationFilePath); } + catch(Exception e) + { + Log.e( + TAG, + "Failure opening destination: " + destinationFilePath + " " + + e.getMessage() ); + return false; + } + + try + { + byte[] buf = new byte[1024]; + int len; + while ((len = in.read(buf)) > 0) out.write(buf, 0, len); + } + catch(IOException e) + { + Log.e( + TAG, + "Failure coping: " + assetPath + " -> " + destinationFilePath + + " " + e.getMessage() ); + return false; + } + + try { in.close(); } + catch(IOException e) + { + Log.e(TAG, "Failure closing: " + assetPath + " " + e.getMessage() ); + return false; + } + + try { out.close(); } + catch(IOException e) + { + Log.e( + TAG, + "Failure closing: " + destinationFilePath + " " + + e.getMessage() ); + return false; + } + + return true; + } + + private static final String TAG = "RetroShare AssetHelper.java"; +} diff --git a/libretroshare/src/rs_android/org/retroshare/service/ErrorConditionWrap.java b/libretroshare/src/rs_android/org/retroshare/service/ErrorConditionWrap.java new file mode 100644 index 000000000..c0830f5d2 --- /dev/null +++ b/libretroshare/src/rs_android/org/retroshare/service/ErrorConditionWrap.java @@ -0,0 +1,48 @@ +/* + * RetroShare + * Copyright (C) 2021 Gioacchino Mazzurco + * Copyright (C) 2021 Asociación Civil Altermundi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-FileCopyrightText: Retroshare Team + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package org.retroshare.service; + +public class ErrorConditionWrap +{ + public ErrorConditionWrap( + int value, String message, String categoryName ) + { + mValue = value; + mMessage = message; + mCategoryName = categoryName; + } + + public int value() { return mValue; } + public String message() { return mMessage; } + public String categoryName() { return mCategoryName; } + + public boolean toBool() { return mValue != 0; } + + @Override + public String toString() + { return String.format("%d", mValue)+" "+mMessage+" [" + mCategoryName+ "]"; } + + private int mValue = 0; + private String mMessage; + private String mCategoryName; +} diff --git a/libretroshare/src/rs_android/org/retroshare/service/RetroShareServiceAndroid.java b/libretroshare/src/rs_android/org/retroshare/service/RetroShareServiceAndroid.java new file mode 100644 index 000000000..72fb57c32 --- /dev/null +++ b/libretroshare/src/rs_android/org/retroshare/service/RetroShareServiceAndroid.java @@ -0,0 +1,138 @@ +/* + * RetroShare + * Copyright (C) 2016-2021 Gioacchino Mazzurco + * Copyright (C) 2021 Asociación Civil Altermundi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-FileCopyrightText: Retroshare Team + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package org.retroshare.service; + +import android.app.Service; +import android.os.IBinder; +import android.os.Bundle; +import android.content.Context; +import android.content.Intent; +import android.util.Log; +import android.app.ActivityManager; +import java.util.Objects; + +public class RetroShareServiceAndroid extends Service +{ + public static final int DEFAULT_JSON_API_PORT = 9092; + public static final String DEFAULT_JSON_API_BINDING_ADDRESS = "127.0.0.1"; + + static { System.loadLibrary("retroshare-service"); } + + public static void start( + Context ctx, int jsonApiPort, String jsonApiBindAddress ) + { + Log.d(TAG, "start"); + Intent intent = new Intent(ctx, RetroShareServiceAndroid.class); + intent.putExtra(JSON_API_PORT_KEY, jsonApiPort); + intent.putExtra(JSON_API_BIND_ADDRESS_KEY, jsonApiBindAddress); + ctx.startService(intent); + } + + public static void stop(Context ctx) + { + Log.d(TAG, "stop"); + ctx.stopService(new Intent(ctx, RetroShareServiceAndroid.class)); + } + + public static boolean isRunning(Context ctx) + { + Log.d(TAG, "isRunning"); + ActivityManager manager = + (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE); + for( ActivityManager.RunningServiceInfo service : + manager.getRunningServices(Integer.MAX_VALUE) ) + if( RetroShareServiceAndroid.class.getName() + .equals(service.service.getClassName()) ) + return true; + return false; + } + + public static Context getServiceContext() + { + if(sServiceContext == null) + Log.e(TAG, "getServiceContext() called before onCreate"); + return Objects.requireNonNull(sServiceContext); + } + + @Override + public int onStartCommand( + Intent intent, int flags, int startId ) + { + if(intent == null) + { + Log.i(TAG, "onStartCommand called without intent"); + return Service.START_REDELIVER_INTENT; + } + + int jsonApiPort = DEFAULT_JSON_API_PORT; + String jsonApiBindAddress = DEFAULT_JSON_API_BINDING_ADDRESS; + + Bundle args = intent.getExtras(); + if(args.containsKey(JSON_API_PORT_KEY)) + jsonApiPort = args.getInt(JSON_API_PORT_KEY); + if(args.containsKey(JSON_API_BIND_ADDRESS_KEY)) + jsonApiBindAddress = + args.getString(JSON_API_BIND_ADDRESS_KEY); + + ErrorConditionWrap ec = nativeStart(jsonApiPort, jsonApiBindAddress); + if(ec.toBool()) Log.e(TAG, "onStartCommand(...) " + ec.toString()); + + return super.onStartCommand(intent, flags, startId); + } + + @Override + public void onCreate () + { + super.onCreate(); + sServiceContext = this; + } + + @Override + public void onDestroy() + { + ErrorConditionWrap ec = nativeStop(); + if(ec.toBool()) Log.e(TAG, "onDestroy() " + ec.toString()); + sServiceContext = null; + super.onDestroy(); + } + + @Override + public IBinder onBind(Intent arg0) { return null; } + + private static final String JSON_API_PORT_KEY = + RetroShareServiceAndroid.class.getCanonicalName() + + "/JSON_API_PORT_KEY"; + + private static final String JSON_API_BIND_ADDRESS_KEY = + RetroShareServiceAndroid.class.getCanonicalName() + + "/JSON_API_BIND_ADDRESS_KEY" ; + + private static final String TAG = "RetroShareServiceAndroid.java"; + + private static Context sServiceContext; + + protected static native ErrorConditionWrap nativeStart( + int jsonApiPort, String jsonApiBindAddress ); + + protected static native ErrorConditionWrap nativeStop(); +} diff --git a/libretroshare/src/rs_android/retroshareserviceandroid.cpp b/libretroshare/src/rs_android/retroshareserviceandroid.cpp new file mode 100644 index 000000000..6e156edca --- /dev/null +++ b/libretroshare/src/rs_android/retroshareserviceandroid.cpp @@ -0,0 +1,103 @@ +/* + * RetroShare Service Android + * Copyright (C) 2016-2021 Gioacchino Mazzurco + * Copyright (C) 2021 Asociación Civil Altermundi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-FileCopyrightText: Retroshare Team + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include +#include + +#include "util/stacktrace.h" +#include "retroshare/rsinit.h" +#include "retroshare/rsiface.h" +#include "util/rsdebug.h" + +#include "rs_android/retroshareserviceandroid.hpp" +#include "rs_android/rsjni.hpp" + + +/*static*/ std::unique_ptr +RetroShareServiceAndroid::sAndroidCoutCerrCatcher = nullptr; + +using ErrorConditionWrap = RsJni::ErrorConditionWrap; + +/*static*/ jni::Local> +RetroShareServiceAndroid::start( + JNIEnv& env, jni::Class&, + jni::jint jsonApiPort, const jni::String& jsonApiBindAddress ) +{ + if(jsonApiPort < 0 || jsonApiPort > std::numeric_limits::max()) + { + RS_ERR("Got invalid JSON API port: ", jsonApiPort); + return jni::Make(env, std::errc::invalid_argument); + } + + RsInfo() << "\n" << + "+================================================================+\n" + "| o---o o |\n" + "| \\ / - Retroshare Service Android - / \\ |\n" + "| o o---o |\n" + "+================================================================+" + << std::endl << std::endl; + + sAndroidCoutCerrCatcher = std::make_unique(); + + RsInit::InitRsConfig(); + RsControl::earlyInitNotificationSystem(); + + RsConfigOptions conf; + conf.jsonApiPort = static_cast(jsonApiPort); + conf.jsonApiBindAddress = jni::Make(env, jsonApiBindAddress); + + // Dirty workaround plugins not supported on Android ATM + conf.main_executable_path = " "; + + int initResult = RsInit::InitRetroShare(conf); + if(initResult != RS_INIT_OK) + { + RS_ERR("Retroshare core initalization failed with: ", initResult); + return jni::Make(env, std::errc::no_child_process); + } + + return jni::Make(env, std::error_condition()); +} + +jni::Local> RetroShareServiceAndroid::stop( + JNIEnv& env, jni::Class& ) +{ + if(RsControl::instance()->isReady()) + { + RsControl::instance()->rsGlobalShutDown(); + return jni::Make(env, std::error_condition()); + } + + sAndroidCoutCerrCatcher.reset(); + + return jni::Make(env, std::errc::no_such_process); +} + +jni::Local > +RetroShareServiceAndroid::getAndroidContext(JNIEnv& env) +{ + auto& clazz = jni::Class::Singleton(env); + static auto method = + clazz.GetStaticMethod()>( + env, "getServiceContext" ); + return clazz.Call(env, method); +} diff --git a/libretroshare/src/rs_android/retroshareserviceandroid.hpp b/libretroshare/src/rs_android/retroshareserviceandroid.hpp new file mode 100644 index 000000000..bd8f88f31 --- /dev/null +++ b/libretroshare/src/rs_android/retroshareserviceandroid.hpp @@ -0,0 +1,84 @@ +/* + * RetroShare Service Android + * Copyright (C) 2016-2021 Gioacchino Mazzurco + * Copyright (C) 2021 Asociación Civil Altermundi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-FileCopyrightText: Retroshare Team + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +#pragma once + +#include +#include + +#include + +#include "rs_android/rsjni.hpp" +#include "rs_android/androidcoutcerrcatcher.hpp" + +#include "util/stacktrace.h" + +/** Provide native methods that are registered into corresponding Java class + * to start/stop RetroShare with reasonable comfort on Android platform */ +struct RetroShareServiceAndroid +{ + static constexpr auto Name() + { return "org/retroshare/service/RetroShareServiceAndroid"; } + + using ErrorConditionWrap = RsJni::ErrorConditionWrap; + + /** + * Called from RetroShareServiceAndroid Java to init libretroshare + * @param[in] env the usual JNI parafernalia + * @param[in] jclass the usual JNI parafernalia + * @param[in] jsonApiPort port on which JSON API server will listen + * @param[in] jsonApiBindAddress binding address of the JSON API server + * @note Yeah you read it well we use a full 32 bit signed integer for JSON + * API port. This is because Java lack even the minimum decency to implement + * unsigned integral types so we need to wrap the port (16 bit unsigned + * integer everywhere reasonable) into a full integer and then check at + * runtime the value. + */ + static jni::Local> start( + JNIEnv& env, jni::Class& jclass, + jni::jint jsonApiPort, const jni::String& jsonApiBindAddress ); + + /** + * Called from RetroShareServiceAndroid Java to shutdown libretroshare + * @param[in] env the usual JNI parafernalia + * @param[in] jclass the usual JNI parafernalia + */ + static jni::Local> stop( + JNIEnv& env, jni::Class& ); + + struct Context + { + /// JNI parafernalia + static constexpr auto Name() { return "android/content/Context"; } + }; + + /// Return RetroShare Service Android Context + static jni::Local> getAndroidContext(JNIEnv& env); + +private: + /** Doesn't involve complex liftime handling stuff better let the runtime + * handle costruction (ASAP)/destruction for us */ + static CrashStackTrace CrashStackTrace; + + /** Involve threads, file descriptors etc. better handle lifetime + * explicitely */ + static std::unique_ptr sAndroidCoutCerrCatcher; +}; diff --git a/libretroshare/src/rs_android/rsjni.cpp b/libretroshare/src/rs_android/rsjni.cpp new file mode 100644 index 000000000..c9fc2c8ed --- /dev/null +++ b/libretroshare/src/rs_android/rsjni.cpp @@ -0,0 +1,70 @@ +/******************************************************************************* + * RetroShare JNI utilities * + * * + * libretroshare: retroshare core library * + * * + * Copyright (C) 2021 Gioacchino Mazzurco * + * Copyright (C) 2021 Asociación Civil Altermundi * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include "rs_android/rsjni.hpp" +#include "rs_android/retroshareserviceandroid.hpp" + +rs_view_ptr RsJni::mJvm = nullptr; + + +extern "C" JNIEXPORT jint JNICALL JNI_OnLoad_retroshare(JavaVM* vm, void*) +{ + RS_DBG(vm); + + RsJni::mJvm = vm; + + jni::JNIEnv& env { jni::GetEnv(*vm) }; + + /** Ensure singleton refereces to our own Java classes are inizialized here + * because default Java class loader which is the one accessible by native + * threads which is not main even if attached, is not capable to find them. + * https://stackoverflow.com/questions/20752352/classnotfoundexception-when-finding-a-class-in-jni-background-thread + * https://groups.google.com/g/android-ndk/c/2gkr1mXKn_E */ + jni::Class::Singleton(env); + jni::Class::Singleton(env); + + jni::RegisterNatives( + env, *jni::Class::Singleton(env), + jni::MakeNativeMethod< + decltype(&RetroShareServiceAndroid::start), + &RetroShareServiceAndroid::start >("nativeStart"), + jni::MakeNativeMethod< + decltype(&RetroShareServiceAndroid::stop), + &RetroShareServiceAndroid::stop >("nativeStop") + ); + + return jni::Unwrap(jni::jni_version_1_2); +} + +#ifdef RS_LIBRETROSHARE_EXPORT_JNI_ONLOAD +/** If libretroshare is linked statically to other components which already + * export JNI_OnLoad then a symbol clash may happen + * if RS_LIBRETROSHARE_EXPORT_JNI_ONLOAD is defined. + * @see JNI_OnLoad_retroshare should instead be called from the exported + * JNI_OnLoad */ +extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* _reserved) +{ + RS_DBG(vm); + return JNI_OnLoad_retroshare(vm, _reserved); +} +#endif // def RS_LIBRETROSHARE_EXPORT_JNI_ONLOAD diff --git a/libretroshare/src/rs_android/rsjni.hpp b/libretroshare/src/rs_android/rsjni.hpp new file mode 100644 index 000000000..339dfd5c7 --- /dev/null +++ b/libretroshare/src/rs_android/rsjni.hpp @@ -0,0 +1,90 @@ +/******************************************************************************* + * RetroShare JNI utilities * + * * + * libretroshare: retroshare core library * + * * + * Copyright (C) 2021 Gioacchino Mazzurco * + * Copyright (C) 2021 Asociación Civil Altermundi * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ +#pragma once + +#include +#include + +#include + +#include "util/rsmemory.h" +#include "util/cxx23retrocompat.h" + + +/** Store JVM pointer safely and register native methods */ +extern "C" JNIEXPORT jint JNICALL JNI_OnLoad_retroshare(JavaVM* vm, void*); + + +/** Provide library wide JVM access with some safe measures + * The JVM pointer is set properly by @see JNI_OnLoad_retroshare + */ +class RsJni +{ +public: + static inline JavaVM& getVM() + { + if(!mJvm) // [[unlikely]] + { + RS_FATAL( "Attempt to access JVM before JNI_OnLoad_retroshare ", + std::errc::bad_address ); + print_stacktrace(); + std::exit(std::to_underlying(std::errc::bad_address)); + } + + return *mJvm; + } + + friend jint JNI_OnLoad_retroshare(JavaVM* vm, void*); + + /** Provide a comfortable way to access Android package assets like + * bdboot.txt from C++ */ + struct AssetHelper + { + static constexpr auto Name() + { return "org/retroshare/service/AssetHelper"; } + }; + + /** Provide a comfortable way to propagate C++ error_conditions to Java + * callers */ + struct ErrorConditionWrap + { + static constexpr auto Name() + { return "org/retroshare/service/ErrorConditionWrap"; } + }; + +private: + static rs_view_ptr mJvm; +}; + + +namespace jni +{ +/** Provides idiomatic way of creating instances via +@code{.cpp} + jni::Make(env, std::error_condition()); +@endcode */ +jni::Local> +MakeAnything( + jni::ThingToMake, JNIEnv& env, + const std::error_condition& ec ); +} diff --git a/libretroshare/src/rsserver/rsaccounts.cc b/libretroshare/src/rsserver/rsaccounts.cc index 8be56073c..1fa3802d1 100644 --- a/libretroshare/src/rsserver/rsaccounts.cc +++ b/libretroshare/src/rsserver/rsaccounts.cc @@ -32,12 +32,12 @@ #include #include - +#include #include #include "retroshare/rsinit.h" #include "rsaccounts.h" - +#include "util/rsdebug.h" #include "util/rsdir.h" #include "util/rsstring.h" #include "util/folderiterator.h" @@ -48,6 +48,11 @@ #include +#ifdef __ANDROID__ +# include "rs_android/rsjni.hpp" +# include "rs_android/retroshareserviceandroid.hpp" +#endif + // Global singleton declaration of data. RsAccountsDetail* RsAccounts::rsAccountsDetails = nullptr; @@ -328,22 +333,7 @@ bool RsAccountsDetail::defaultBaseDirectory() { std::string basedir; -/******************************** WINDOWS/UNIX SPECIFIC PART ******************/ -#ifndef WINDOWS_SYS - - // unix: homedir + /.retroshare - char *h = getenv("HOME"); - if (h == NULL) - { - std::cerr << "defaultBaseDirectory() Error: cannot determine $HOME dir" - << std::endl; - return false ; - } - - basedir = h; - basedir += "/.retroshare"; - -#else +#ifdef WINDOWS_SYS if (RsInit::isPortable()) { // use directory "Data" in portable version @@ -375,13 +365,53 @@ bool RsAccountsDetail::defaultBaseDirectory() } basedir += "\\RetroShare"; } -#endif -/******************************** WINDOWS/UNIX SPECIFIC PART ******************/ +#elif defined (__ANDROID__) // def WINDOWS_SYS + + struct ApplicationInfo + { + static constexpr auto Name() + { return "android/content/pm/ApplicationInfo"; } + }; + + auto uenv = jni::GetAttachedEnv(RsJni::getVM()); + JNIEnv& env = *uenv; + auto androidContext = RetroShareServiceAndroid::getAndroidContext(env); + auto& contextClass = + jni::Class::Singleton(env); + + auto& applicationInfoClass = jni::Class::Singleton(env); + + auto getApplicationInfo = + contextClass.GetMethod ()>( + env, "getApplicationInfo" ); + + auto applicationInfo = androidContext.Call(env, getApplicationInfo); + + auto dataDirField = jni::Field( + env, applicationInfoClass, "dataDir" ); + + jni::Local dataDir = applicationInfo.Get( + env, dataDirField ); + + basedir = jni::Make(env, dataDir) + "/.retroshare/"; + +#else // def WINDOWS_SYS, if defined (__ANDROID__) + // unix: homedir + /.retroshare + char* h = getenv("HOME"); + if(h == nullptr) + { + RS_ERR("cannot determine $HOME dir"); + return false ; + } + + basedir = h; + basedir += "/.retroshare"; +#endif // def WINDOWS_SYS /* store to class variable */ mBaseDirectory = basedir; - std::cerr << "defaultBaseDirectory() = " << mBaseDirectory; - std::cerr << std::endl; + + RS_INFO(mBaseDirectory); return true; } @@ -941,6 +971,11 @@ bool RsAccountsDetail::exportIdentityToString( bool RsAccountsDetail::copyGnuPGKeyrings() { +#ifdef __ANDROID__ + RS_ERR(std::errc::not_supported); + print_stacktrace(); + return false; +#else std::string pgp_dir = PathPGPDirectory() ; if(!RsDirUtil::checkCreateDirectory(pgp_dir)) @@ -992,6 +1027,7 @@ bool RsAccountsDetail::copyGnuPGKeyrings() } return true ; +#endif // def __ANDROID__ } diff --git a/libretroshare/src/rsserver/rsinit.cc b/libretroshare/src/rsserver/rsinit.cc index 7433ab151..67452d8b7 100644 --- a/libretroshare/src/rsserver/rsinit.cc +++ b/libretroshare/src/rsserver/rsinit.cc @@ -32,8 +32,9 @@ #endif #ifdef __ANDROID__ -# include // To install bdboot.txt -# include // for QString::fromStdString(...) +# include +# include "rs_android/rsjni.hpp" +# include "rs_android/retroshareserviceandroid.hpp" #endif #include "util/argstream.h" @@ -194,7 +195,7 @@ static const int SSLPWD_LEN = 64; void RsInit::InitRsConfig() { - RsInfo() << " libretroshare version: " << RS_HUMAN_READABLE_VERSION + RsInfo() << "libretroshare version: " << RS_HUMAN_READABLE_VERSION << std::endl; rsInitConfig = new RsInitConfig; @@ -1011,32 +1012,32 @@ int RsServer::StartupRetroShare() uint64_t tmp_size ; if (!RsDirUtil::checkFile(bootstrapfile,tmp_size,true)) { - std::cerr << "DHT bootstrap file not in ConfigDir: " << bootstrapfile - << std::endl; -#ifdef __ANDROID__ - QFile bdbootRF("assets:/values/bdboot.txt"); - if(!bdbootRF.open(QIODevice::ReadOnly | QIODevice::Text)) - std::cerr << __PRETTY_FUNCTION__ - << " bdbootRF(assets:/values/bdboot.txt).open(...) fail: " - << bdbootRF.errorString().toStdString() << std::endl; - else - { - QFile bdbootCF(QString::fromStdString(bootstrapfile)); - if(!bdbootCF.open(QIODevice::WriteOnly | QIODevice::Text)) - std::cerr << __PRETTY_FUNCTION__ << " bdbootCF(" - << bootstrapfile << ").open(...) fail: " - << bdbootRF.errorString().toStdString() << std::endl; - else - { - bdbootCF.write(bdbootRF.readAll()); - bdbootCF.close(); - std::cerr << "Installed DHT bootstrap file not in ConfigDir: " - << bootstrapfile << std::endl; - } + RS_INFO("DHT bootstrap file not in ConfigDir: ", bootstrapfile); - bdbootRF.close(); - } -#else +#ifdef __ANDROID__ + auto uenv = jni::GetAttachedEnv(RsJni::getVM()); + JNIEnv& env = *uenv; + + using AContext = RetroShareServiceAndroid::Context; + + auto& assetHelperClass = jni::Class::Singleton(env); + + static auto copyAsset = + assetHelperClass.GetStaticMethod< + jni::jboolean(jni::Object, jni::String, jni::String)>( + env, "copyAsset" ); + + auto androidContext = RetroShareServiceAndroid::getAndroidContext(env); + + jni::jboolean result = assetHelperClass.Call( + env, copyAsset, + androidContext, + jni::Make(env, "values/bdboot.txt"), + jni::Make(env, bootstrapfile) ); + + if(!result) RS_ERR("Failure installing ", bootstrapfile); + +#else // def __ANDROID__ std::cerr << "Checking for Installation DHT bootstrap file " << installfile << std::endl; if ((installfile != "") && (RsDirUtil::checkFile(installfile,tmp_size))) { diff --git a/libretroshare/src/services/broadcastdiscoveryservice.cc b/libretroshare/src/services/broadcastdiscoveryservice.cc index 076244b44..98b67cdcd 100644 --- a/libretroshare/src/services/broadcastdiscoveryservice.cc +++ b/libretroshare/src/services/broadcastdiscoveryservice.cc @@ -1,7 +1,8 @@ /******************************************************************************* * RetroShare Broadcast Domain Discovery * * * - * Copyright (C) 2019 Gioacchino Mazzurco * + * Copyright (C) 2019-2021 Gioacchino Mazzurco * + * Copyright (C) 2019-2021 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -25,16 +26,17 @@ #include #include -#ifdef __ANDROID__ -# include -#endif // def __ANDROID__ - #include "services/broadcastdiscoveryservice.h" #include "retroshare/rspeers.h" #include "serialiser/rsserializable.h" #include "serialiser/rsserializer.h" #include "retroshare/rsevents.h" +#ifdef __ANDROID__ +# include "rs_android/retroshareserviceandroid.hpp" +#endif // def __ANDROID__ + + /*extern*/ RsBroadcastDiscovery* rsBroadcastDiscovery = nullptr; struct BroadcastDiscoveryPack : RsSerializable @@ -99,7 +101,7 @@ BroadcastDiscoveryService::BroadcastDiscoveryService( if(mRsPeers.isHiddenNode(mRsPeers.getOwnId())) return; #ifdef __ANDROID__ - createMulticastLock(); + createAndroidMulticastLock(); #endif // def __ANDROID__ enableMulticastListening(); @@ -228,19 +230,47 @@ RsBroadcastDiscoveryResult BroadcastDiscoveryService::createResult( bool BroadcastDiscoveryService::isMulticastListeningEnabled() { #ifdef __ANDROID__ - return assertMulticastLockIsvalid() && - mWifiMulticastLock.callMethod("isHeld"); -#endif // def __ANDROID__ + if(!mAndroidWifiMulticastLock) + { + RS_ERR("Android multicast lock not initialized!"); + return false; + } + auto uenv = jni::GetAttachedEnv(RsJni::getVM()); + JNIEnv& env = *uenv; + auto& multicastLockClass = jni::Class::Singleton(env); + + auto isHeld = + multicastLockClass.GetMethod( + env, "isHeld" ); + + return mAndroidWifiMulticastLock.Call(env, isHeld); +#else if // def __ANDROID__ return true; +#endif // def __ANDROID__ } bool BroadcastDiscoveryService::enableMulticastListening() { #ifdef __ANDROID__ - if(assertMulticastLockIsvalid() && !isMulticastListeningEnabled()) + if(!mAndroidWifiMulticastLock) { - mWifiMulticastLock.callMethod("acquire"); + RS_ERR("Android multicast lock not initialized!"); + return false; + } + + if(!isMulticastListeningEnabled()) + { + auto uenv = jni::GetAttachedEnv(RsJni::getVM()); + JNIEnv& env = *uenv; + auto& multicastLockClass = jni::Class::Singleton(env); + + auto acquire = + multicastLockClass.GetMethod( + env, "acquire" ); + + mAndroidWifiMulticastLock.Call(env, acquire); + return true; } #endif // def __ANDROID__ @@ -251,9 +281,24 @@ bool BroadcastDiscoveryService::enableMulticastListening() bool BroadcastDiscoveryService::disableMulticastListening() { #ifdef __ANDROID__ - if(assertMulticastLockIsvalid() && isMulticastListeningEnabled()) + if(!mAndroidWifiMulticastLock) { - mWifiMulticastLock.callMethod("release"); + RS_ERR("Android multicast lock not initialized!"); + return false; + } + + if(isMulticastListeningEnabled()) + { + auto uenv = jni::GetAttachedEnv(RsJni::getVM()); + JNIEnv& env = *uenv; + auto& multicastLockClass = jni::Class::Singleton(env); + + auto release = + multicastLockClass.GetMethod( + env, "release" ); + + mAndroidWifiMulticastLock.Call(env, release); + return true; } #endif // def __ANDROID__ @@ -262,56 +307,57 @@ bool BroadcastDiscoveryService::disableMulticastListening() } #ifdef __ANDROID__ -bool BroadcastDiscoveryService::createMulticastLock() + +bool BroadcastDiscoveryService::createAndroidMulticastLock() { - Dbg2() << __PRETTY_FUNCTION__ << std::endl; - - constexpr auto fname = __PRETTY_FUNCTION__; - const auto failure = [&](const std::string& err) + if(mAndroidWifiMulticastLock) { - RsErr() << fname << " " << err << std::endl; - return false; - }; - - if(mWifiMulticastLock.isValid()) - return failure("mWifiMulticastLock is already initialized"); - - QAndroidJniObject context = QtAndroid::androidContext(); - if(!context.isValid()) - return failure("Cannot retrieve Android context"); - - QAndroidJniObject WIFI_SERVICE = QAndroidJniObject::getStaticObjectField( - "android.content.Context", "WIFI_SERVICE", "Ljava/lang/String;"); - if(!WIFI_SERVICE.isValid()) - return failure("Cannot retrieve Context.WIFI_SERVICE value"); - - QAndroidJniObject wifiManager = context.callObjectMethod( - "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;", - WIFI_SERVICE.object() ); - if(!wifiManager.isValid()) - return failure("Cannot retrieve Android Wifi Manager"); - - mWifiMulticastLock = wifiManager.callObjectMethod( - "createMulticastLock", - "(Ljava/lang/String;)Landroid/net/wifi/WifiManager$MulticastLock;", - QAndroidJniObject::fromString(fname).object() ); - if(!mWifiMulticastLock.isValid()) - return failure("Cannot create WifiManager.MulticastLock"); - - return true; -} - -bool BroadcastDiscoveryService::assertMulticastLockIsvalid() -{ - if(!mWifiMulticastLock.isValid()) - { - RsErr() << __PRETTY_FUNCTION__ << " mWifiMulticastLock is invalid!" - << std::endl; + RS_ERR("Android multicast lock is already initialized"); print_stacktrace(); return false; } + + auto uenv = jni::GetAttachedEnv(RsJni::getVM()); + JNIEnv& env = *uenv; + + using AContextTag = RetroShareServiceAndroid::Context; + using AContext = jni::Class; + static auto& contextClass = AContext::Singleton(env); + + auto wifiServiceField = jni::StaticField( + env, contextClass, "WIFI_SERVICE"); + + jni::Local WIFI_SERVICE = contextClass.Get( + env, wifiServiceField ); + + auto androidContext = RetroShareServiceAndroid::getAndroidContext(env); + + auto getSystemService = + contextClass.GetMethod (jni::String)>( + env, "getSystemService" ); + + struct WifiManager + { static constexpr auto Name() { return "android/net/wifi/WifiManager"; } }; + + auto& wifiManagerClass = jni::Class::Singleton(env); + + auto wifiManager = jni::Cast( + env, wifiManagerClass, + androidContext.Call(env, getSystemService, WIFI_SERVICE) ); + + auto createMulticastLock = + wifiManagerClass.GetMethod(jni::String)>( + env, "createMulticastLock" ); + + mAndroidWifiMulticastLock = jni::NewGlobal( + env, wifiManager.Call( + env, createMulticastLock, + jni::Make( + env, "RetroShare BroadcastDiscoveryService" ) ) ); + return true; } + #endif // def __ANDROID__ RsBroadcastDiscovery::~RsBroadcastDiscovery() = default; diff --git a/libretroshare/src/services/broadcastdiscoveryservice.h b/libretroshare/src/services/broadcastdiscoveryservice.h index ebbec04c1..f4204c324 100644 --- a/libretroshare/src/services/broadcastdiscoveryservice.h +++ b/libretroshare/src/services/broadcastdiscoveryservice.h @@ -1,7 +1,8 @@ /******************************************************************************* * RetroShare Broadcast Domain Discovery * * * - * Copyright (C) 2019 Gioacchino Mazzurco * + * Copyright (C) 2019-2021 Gioacchino Mazzurco * + * Copyright (C) 2019-2021 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -27,14 +28,16 @@ #include -#ifdef __ANDROID__ -# include -#endif // def __ANDROID__ - #include "retroshare/rsbroadcastdiscovery.h" #include "util/rsthreads.h" #include "util/rsdebug.h" +#ifdef __ANDROID__ +# include +# include "rs_android/rsjni.hpp" +#endif // def __ANDROID__ + + namespace UDC = udpdiscovery; class RsPeers; @@ -42,7 +45,7 @@ class BroadcastDiscoveryService : public RsBroadcastDiscovery, public RsTickingThread { public: - BroadcastDiscoveryService(RsPeers& pRsPeers); + explicit BroadcastDiscoveryService(RsPeers& pRsPeers); ~BroadcastDiscoveryService() override; /// @see RsBroadcastDiscovery @@ -71,26 +74,27 @@ protected: std::map mDiscoveredData; RsMutex mDiscoveredDataMutex; - RsPeers& mRsPeers; // TODO: std::shared_ptr mRsPeers; + RsPeers& mRsPeers; RsBroadcastDiscoveryResult createResult( const UDC::IpPort& ipp, const std::string& uData ); #ifdef __ANDROID__ - /** Android WifiManager.MulticastLock */ - QAndroidJniObject mWifiMulticastLock; + struct AndroidMulticastLock + { + static constexpr auto Name() + { return "android/net/wifi/WifiManager$MulticastLock"; } + }; + + jni::Global> mAndroidWifiMulticastLock; /** Initialize the wifi multicast lock without acquiring it * Needed to enable multicast listening in Android, for RetroShare broadcast * discovery inspired by: * https://github.com/flutter/flutter/issues/16335#issuecomment-420547860 */ - bool createMulticastLock(); - - /** Return false if mWifiMulticastLock is invalid and print error messages */ - bool assertMulticastLockIsvalid(); - -#endif // def __ANDROID__ + bool createAndroidMulticastLock(); +#endif RS_SET_CONTEXT_DEBUG_LEVEL(3) }; diff --git a/libretroshare/src/use_libretroshare.pri b/libretroshare/src/use_libretroshare.pri index a34951772..7ceda74ba 100644 --- a/libretroshare/src/use_libretroshare.pri +++ b/libretroshare/src/use_libretroshare.pri @@ -111,14 +111,7 @@ PRE_TARGETDEPS += $$pretargetStaticLibs(sLibs) LIBS += $$linkDynamicLibs(dLibs) android-* { - CONFIG *= qt - - lessThan(ANDROID_API_VERSION, 24) { - ## @See: android_ifaddrs/README.adoc - contains(DEFINES, LIBRETROSHARE_ANDROID_IFADDRS_QT) { - QT *= network - } - } + INCLUDEPATH *= $$clean_path($${RS_SRC_PATH}/supportlibs/jni.hpp/include/) } ################################### Pkg-Config Stuff ############################# diff --git a/retroshare-service/src/android/AndroidManifest.xml b/retroshare-service/src/android/AndroidManifest.xml index d41a72995..ecf8ba78a 100644 --- a/retroshare-service/src/android/AndroidManifest.xml +++ b/retroshare-service/src/android/AndroidManifest.xml @@ -2,9 +2,9 @@ - + @@ -14,59 +14,81 @@ - - - - - - - - - - - - - + + + + + - + + + + - - - - +]]> + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + @@ -76,17 +98,11 @@ ++ features based on the dependencies of the application. ++ Remove the comment if you do not require these default features. --> +]]> - + - - - - - - - - - + --> +]]> diff --git a/retroshare-service/src/android/src/org/retroshare/service/AppUpdatedReceiver.java b/retroshare-service/src/android/src/org/retroshare/service/AppUpdatedReceiver.java index 283fd10cc..3f2e3f375 100644 --- a/retroshare-service/src/android/src/org/retroshare/service/AppUpdatedReceiver.java +++ b/retroshare-service/src/android/src/org/retroshare/service/AppUpdatedReceiver.java @@ -19,6 +19,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +/* package org.retroshare.service; import android.content.BroadcastReceiver; @@ -36,3 +37,4 @@ public class AppUpdatedReceiver extends BroadcastReceiver RetroShareServiceAndroid.start(context); } } +*/ \ No newline at end of file diff --git a/retroshare-service/src/android/src/org/retroshare/service/AssetHelper.java b/retroshare-service/src/android/src/org/retroshare/service/AssetHelper.java new file mode 120000 index 000000000..60da18398 --- /dev/null +++ b/retroshare-service/src/android/src/org/retroshare/service/AssetHelper.java @@ -0,0 +1 @@ +../../../../../../../libretroshare/src/rs_android/org/retroshare/service/AssetHelper.java \ No newline at end of file diff --git a/retroshare-service/src/android/src/org/retroshare/service/BootCompletedReceiver.java b/retroshare-service/src/android/src/org/retroshare/service/BootCompletedReceiver.java index 89c09c241..75bfbc063 100644 --- a/retroshare-service/src/android/src/org/retroshare/service/BootCompletedReceiver.java +++ b/retroshare-service/src/android/src/org/retroshare/service/BootCompletedReceiver.java @@ -19,6 +19,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +/* package org.retroshare.service; import android.content.BroadcastReceiver; @@ -35,3 +36,4 @@ public class BootCompletedReceiver extends BroadcastReceiver RetroShareServiceAndroid.start(context); } } +*/ \ No newline at end of file diff --git a/retroshare-service/src/android/src/org/retroshare/service/ErrorConditionWrap.java b/retroshare-service/src/android/src/org/retroshare/service/ErrorConditionWrap.java new file mode 120000 index 000000000..1f90a62c6 --- /dev/null +++ b/retroshare-service/src/android/src/org/retroshare/service/ErrorConditionWrap.java @@ -0,0 +1 @@ +../../../../../../../libretroshare/src/rs_android/org/retroshare/service/ErrorConditionWrap.java \ No newline at end of file diff --git a/retroshare-service/src/android/src/org/retroshare/service/RetroShareServiceAndroid.java b/retroshare-service/src/android/src/org/retroshare/service/RetroShareServiceAndroid.java deleted file mode 100644 index 3cea999bf..000000000 --- a/retroshare-service/src/android/src/org/retroshare/service/RetroShareServiceAndroid.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * RetroShare - * Copyright (C) 2016-2018 Gioacchino Mazzurco - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - * SPDX-FileCopyrightText: Retroshare Team - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -package org.retroshare.service; - -import android.app.ActivityManager; -import android.content.Context; -import android.content.Intent; - -import org.qtproject.qt5.android.bindings.QtService; - -public class RetroShareServiceAndroid extends QtService -{ - public static void start(Context ctx) - { - ctx.startService(new Intent(ctx, RetroShareServiceAndroid.class)); - } - - public static void stop(Context ctx) - { - ctx.stopService(new Intent(ctx, RetroShareServiceAndroid.class)); - } - - public static boolean isRunning(Context ctx) - { - ActivityManager manager = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE); - for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) - if (RetroShareServiceAndroid.class.getName().equals(service.service.getClassName())) - return true; - return false; - } -} diff --git a/retroshare-service/src/android/src/org/retroshare/service/RetroShareServiceAndroid.java b/retroshare-service/src/android/src/org/retroshare/service/RetroShareServiceAndroid.java new file mode 120000 index 000000000..480e21ac4 --- /dev/null +++ b/retroshare-service/src/android/src/org/retroshare/service/RetroShareServiceAndroid.java @@ -0,0 +1 @@ +../../../../../../../libretroshare/src/rs_android/org/retroshare/service/RetroShareServiceAndroid.java \ No newline at end of file diff --git a/retroshare-service/src/android/src/org/retroshare/service/RetroShareServiceControlActivity.java b/retroshare-service/src/android/src/org/retroshare/service/RetroShareServiceControlActivity.java index d1e1f78b7..dafe10301 100644 --- a/retroshare-service/src/android/src/org/retroshare/service/RetroShareServiceControlActivity.java +++ b/retroshare-service/src/android/src/org/retroshare/service/RetroShareServiceControlActivity.java @@ -1,6 +1,6 @@ /* * RetroShare - * Copyright (C) 2016-2018 Gioacchino Mazzurco + * Copyright (C) 2016-2021 Gioacchino Mazzurco * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -50,7 +50,11 @@ public class RetroShareServiceControlActivity extends Activity else { button.setText("Starting..."); - RetroShareServiceAndroid.start(RetroShareServiceControlActivity.this); + RetroShareServiceAndroid.start( + RetroShareServiceControlActivity.this, + RetroShareServiceAndroid.DEFAULT_JSON_API_PORT, + RetroShareServiceAndroid.DEFAULT_JSON_API_BINDING_ADDRESS + ); serviceStarting = true; serviceStopping = false; } diff --git a/retroshare-service/src/retroshare-service-android.cc b/retroshare-service/src/retroshare-service-android.cc new file mode 100644 index 000000000..30204c8fd --- /dev/null +++ b/retroshare-service/src/retroshare-service-android.cc @@ -0,0 +1,29 @@ +/* + * RetroShare Service Android + * Copyright (C) 2021 Gioacchino Mazzurco + * Copyright (C) 2021 Asociación Civil Altermundi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-FileCopyrightText: Retroshare Team + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include "rs_android/rsjni.hpp" + +extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* _reserved) +{ + RS_DBG(vm); + return JNI_OnLoad_retroshare(vm, _reserved); +} diff --git a/retroshare-service/src/retroshare-service.cc b/retroshare-service/src/retroshare-service.cc index e80d468e1..3aad20a8c 100644 --- a/retroshare-service/src/retroshare-service.cc +++ b/retroshare-service/src/retroshare-service.cc @@ -1,6 +1,7 @@ /* * RetroShare Service - * Copyright (C) 2016-2019 Gioacchino Mazzurco + * Copyright (C) 2016-2021 Gioacchino Mazzurco + * Copyright (C) 2021 Asociación Civil Altermundi * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -19,39 +20,31 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -#include "util/stacktrace.h" -#include "util/argstream.h" -#include "util/rskbdinput.h" -#include "retroshare/rsinit.h" - -#ifdef RS_JSONAPI -#include "retroshare/rsjsonapi.h" - -#ifdef RS_WEBUI -#include "retroshare/rswebui.h" -#endif -#endif - -static CrashStackTrace gCrashStackTrace; #include #include #include #include -#ifdef __ANDROID__ -# include -# include -# include -# include - -# include "util/androiddebug.h" -#endif // def __ANDROID__ - +#include "util/stacktrace.h" +#include "util/argstream.h" +#include "util/rskbdinput.h" +#include "retroshare/rsinit.h" #include "retroshare/rsinit.h" #include "retroshare/rsiface.h" #include "util/rsdebug.h" +#ifdef RS_JSONAPI +# include "retroshare/rsjsonapi.h" + +# ifdef RS_WEBUI +# include "retroshare/rswebui.h" +# endif // def RS_WEBUI +#endif // def RS_JSONAPI + +static CrashStackTrace gCrashStackTrace; + + #ifdef RS_SERVICE_TERMINAL_LOGIN class RsServiceNotify: public NotifyClient { @@ -74,9 +67,6 @@ public: }; #endif // def RS_SERVICE_TERMINAL_LOGIN -#ifdef __ANDROID__ -void signalHandler(int /*signal*/) { QCoreApplication::exit(0); } -#else static std::atomic keepRunning(true); static int receivedSignal = 0; @@ -87,16 +77,10 @@ void signalHandler(int signal) receivedSignal = signal; keepRunning = false; } -#endif // def __ANDROID__ int main(int argc, char* argv[]) { -#ifdef __ANDROID__ - AndroidStdIOCatcher dbg; (void) dbg; - QAndroidService app(argc, argv); -#endif // def __ANDROID__ - signal(SIGINT, signalHandler); signal(SIGTERM, signalHandler); #ifdef SIGBREAK @@ -128,7 +112,7 @@ int main(int argc, char* argv[]) RsConfigOptions conf; #ifdef RS_JSONAPI - conf.jsonApiPort = RsJsonApi::DEFAULT_PORT; // enable JSonAPI by default + conf.jsonApiPort = RsJsonApi::DEFAULT_PORT; // enable JSON API by default #ifdef RS_WEBUI std::string webui_base_directory = RsWebUi::DEFAULT_BASE_DIRECTORY; #endif @@ -323,22 +307,10 @@ int main(int argc, char* argv[]) } #endif -#ifdef __ANDROID__ - rsControl->setShutdownCallback(QCoreApplication::exit); - - QObject::connect( - &app, &QCoreApplication::aboutToQuit, - [](){ - if(RsControl::instance()->isReady()) - RsControl::instance()->rsGlobalShutDown(); } ); - - return app.exec(); -#else // def __ANDROID__ rsControl->setShutdownCallback([&](int){keepRunning = false;}); while(keepRunning) std::this_thread::sleep_for(std::chrono::milliseconds(500)); return 0; -#endif } diff --git a/retroshare-service/src/retroshare-service.pro b/retroshare-service/src/retroshare-service.pro index cbd1b9d13..64d373e76 100644 --- a/retroshare-service/src/retroshare-service.pro +++ b/retroshare-service/src/retroshare-service.pro @@ -1,6 +1,6 @@ # RetroShare service qmake build script # -# Copyright (C) 2018-2019, Gioacchino Mazzurco +# Copyright (C) 2018-2021, Gioacchino Mazzurco # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Affero General Public License as published by the @@ -22,8 +22,7 @@ TARGET = retroshare-service -QT += core -QT -= gui +CONFIG -= qt !include("../../libretroshare/src/use_libretroshare.pri"):error("Including") @@ -32,8 +31,6 @@ SOURCES += retroshare-service.cc ################################# Linux ########################################## android-* { - QT += androidextras - ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android DISTFILES += android/AndroidManifest.xml \ @@ -45,6 +42,9 @@ android-* { android/build.gradle \ android/gradle/wrapper/gradle-wrapper.properties \ android/gradlew.bat + + SOURCES -= retroshare-service.cc + SOURCES += retroshare-service-android.cc } diff --git a/supportlibs/jni.hpp b/supportlibs/jni.hpp new file mode 160000 index 000000000..66f73a6aa --- /dev/null +++ b/supportlibs/jni.hpp @@ -0,0 +1 @@ +Subproject commit 66f73a6aa82367d6ba23e7e842f95dfb33c451d6