Do not depend on Qt at all on Android

Do the necessary modifications to not depend on Qt to run on Android
  both in libretroshare and in retroshare-service
This commit is contained in:
Gioacchino Mazzurco 2021-11-30 11:03:22 +01:00
parent 941ec42502
commit 856ce2ffb1
No known key found for this signature in database
GPG Key ID: A1FBCA3872E87051
36 changed files with 1094 additions and 352 deletions

3
.gitmodules vendored
View File

@ -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

View File

@ -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 $?
}

View File

@ -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;
}

View File

@ -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
}

View File

@ -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,

View File

@ -21,16 +21,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#ifdef WINDOWS_SYS
# include "util/rswin.h"
# include "util/rsmemory.h"
# include <ws2tcpip.h>
#endif // WINDOWS_SYS
/// @See: android_ifaddrs/README.adoc
#ifdef __ANDROID__
# include <android/api-level.h>
#endif // def __ANDROID__
#include <cerrno>
#include <iostream>
@ -44,6 +34,28 @@
#include "util/rsnet.h"
#include "util/stacktrace.h"
#ifdef WINDOWS_SYS
# include "util/rswin.h"
# include "util/rsmemory.h"
# include <ws2tcpip.h>
#endif // WINDOWS_SYS
/// @See: android_ifaddrs/README.adoc
#ifdef __ANDROID__
# include <android/api-level.h>
#endif // def __ANDROID__
#ifdef WINDOWS_SYS /* Windows - define errno */
int errno;
#else /* Windows - define errno */
#include <netdb.h>
#endif
#ifdef __HAIKU__
# include <sys/sockio.h>
# 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 <netdb.h>
#endif
#ifdef __HAIKU__
#include <sys/sockio.h>
#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 <sys/types.h>
#ifdef WINDOWS_SYS
# include <winsock2.h>
# include <iphlpapi.h>
# 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 <string>
# include <QString>
# include <QHostAddress>
# include <QNetworkInterface>
#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 <ifaddrs.h>
# include <net/if.h>
#endif // WINDOWS_SYS
@ -331,17 +320,7 @@ bool getLocalAddresses(std::vector<sockaddr_storage>& 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<sockaddr_storage>& addrs)
{
sockaddr_storage tmp;
sockaddr_storage_clear(tmp);
if (sockaddr_storage_copyip(tmp, *reinterpret_cast<sockaddr_storage*>(ifa->ifa_addr)))
if(sockaddr_storage_copyip(
tmp,
*reinterpret_cast<sockaddr_storage*>(ifa->ifa_addr) ))
addrs.push_back(tmp);
}
freeifaddrs(ifsaddrs);
#endif // WINDOWS_SYS
#ifdef NET_DEBUG
std::list<sockaddr_storage>::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();

View File

@ -4,7 +4,8 @@
* libretroshare: retroshare core library *
* *
* Copyright (C) 2004-2006 Robert Fernie <retroshare@lunamutt.com> *
* Copyright (C) 2015-2018 Gioacchino Mazzurco <gio@eigenlab.org> *
* Copyright (C) 2015-2021 Gioacchino Mazzurco <gio@eigenlab.org> *
* Copyright (C) 2021 Asociación Civil Altermundi <info@altermundi.net> *
* *
* 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 <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#ifndef MRK_PQI_NETWORKING_HEADER
#define MRK_PQI_NETWORKING_HEADER
#pragma once
#include <vector>
@ -86,9 +86,13 @@ extern int errno; /* Define extern errno, to duplicate unix behaviour */
#include <string>
#include <list>
#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<sockaddr_storage> & 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

View File

@ -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.

View File

@ -1,9 +1,9 @@
/*******************************************************************************
* libretroshare/src/util: androiddebug.h *
* *
* libretroshare: retroshare core library *
* *
* Copyright (C) 2016 Gioacchino Mazzurco <gio@eigenlab.org> *
* Copyright (C) 2016-2021 Gioacchino Mazzurco <gio@eigenlab.org> *
* Copyright (C) 2021 Asociación Civil Altermundi <info@altermundi.net> *
* *
* 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<bool> 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<AndroidStdIOCatcher*>(instance);
AndroidCoutCerrCatcher &i = *static_cast<AndroidCoutCerrCatcher*>(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;
}
};

View File

@ -0,0 +1,42 @@
/*******************************************************************************
* *
* libretroshare: retroshare core library *
* *
* Copyright (C) 2021 Gioacchino Mazzurco <gio@eigenlab.org> *
* Copyright (C) 2021 Asociación Civil Altermundi <info@altermundi.net> *
* *
* 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 <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#include "rs_android/rsjni.hpp"
namespace jni
{
Local<Object<RsJni::ErrorConditionWrap>> MakeAnything(
ThingToMake<RsJni::ErrorConditionWrap>, JNIEnv& env,
const std::error_condition& ec )
{
auto& clazz = jni::Class<RsJni::ErrorConditionWrap>::Singleton(env);
static auto method =
clazz.GetConstructor<jni::jint, jni::String, jni::String>(env);
jni::jint value = ec.value();
auto message = jni::Make<jni::String>(env, ec.message());
auto category = jni::Make<jni::String>(env, ec.category().name());
return clazz.New(env, method, value, message, category);
}
}

View File

@ -0,0 +1,98 @@
/*
* RetroShare
* Copyright (C) 2021 Gioacchino Mazzurco <gio@eigenlab.org>
* Copyright (C) 2021 Asociación Civil Altermundi <info@altermundi.net>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-FileCopyrightText: Retroshare Team <contact@retroshare.cc>
* 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";
}

View File

@ -0,0 +1,48 @@
/*
* RetroShare
* Copyright (C) 2021 Gioacchino Mazzurco <gio@eigenlab.org>
* Copyright (C) 2021 Asociación Civil Altermundi <info@altermundi.net>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-FileCopyrightText: Retroshare Team <contact@retroshare.cc>
* 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;
}

View File

@ -0,0 +1,138 @@
/*
* RetroShare
* Copyright (C) 2016-2021 Gioacchino Mazzurco <gio@eigenlab.org>
* Copyright (C) 2021 Asociación Civil Altermundi <info@altermundi.net>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-FileCopyrightText: Retroshare Team <contact@retroshare.cc>
* 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();
}

View File

@ -0,0 +1,103 @@
/*
* RetroShare Service Android
* Copyright (C) 2016-2021 Gioacchino Mazzurco <gio@eigenlab.org>
* Copyright (C) 2021 Asociación Civil Altermundi <info@altermundi.net>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-FileCopyrightText: Retroshare Team <contact@retroshare.cc>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <limits>
#include <cstdint>
#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<AndroidCoutCerrCatcher>
RetroShareServiceAndroid::sAndroidCoutCerrCatcher = nullptr;
using ErrorConditionWrap = RsJni::ErrorConditionWrap;
/*static*/ jni::Local<jni::Object<ErrorConditionWrap>>
RetroShareServiceAndroid::start(
JNIEnv& env, jni::Class<RetroShareServiceAndroid>&,
jni::jint jsonApiPort, const jni::String& jsonApiBindAddress )
{
if(jsonApiPort < 0 || jsonApiPort > std::numeric_limits<uint16_t>::max())
{
RS_ERR("Got invalid JSON API port: ", jsonApiPort);
return jni::Make<ErrorConditionWrap>(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<AndroidCoutCerrCatcher>();
RsInit::InitRsConfig();
RsControl::earlyInitNotificationSystem();
RsConfigOptions conf;
conf.jsonApiPort = static_cast<uint16_t>(jsonApiPort);
conf.jsonApiBindAddress = jni::Make<std::string>(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<ErrorConditionWrap>(env, std::errc::no_child_process);
}
return jni::Make<ErrorConditionWrap>(env, std::error_condition());
}
jni::Local<jni::Object<ErrorConditionWrap>> RetroShareServiceAndroid::stop(
JNIEnv& env, jni::Class<RetroShareServiceAndroid>& )
{
if(RsControl::instance()->isReady())
{
RsControl::instance()->rsGlobalShutDown();
return jni::Make<ErrorConditionWrap>(env, std::error_condition());
}
sAndroidCoutCerrCatcher.reset();
return jni::Make<ErrorConditionWrap>(env, std::errc::no_such_process);
}
jni::Local<jni::Object<RetroShareServiceAndroid::Context> >
RetroShareServiceAndroid::getAndroidContext(JNIEnv& env)
{
auto& clazz = jni::Class<RetroShareServiceAndroid>::Singleton(env);
static auto method =
clazz.GetStaticMethod<jni::Object<RetroShareServiceAndroid::Context>()>(
env, "getServiceContext" );
return clazz.Call(env, method);
}

View File

@ -0,0 +1,84 @@
/*
* RetroShare Service Android
* Copyright (C) 2016-2021 Gioacchino Mazzurco <gio@eigenlab.org>
* Copyright (C) 2021 Asociación Civil Altermundi <info@altermundi.net>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-FileCopyrightText: Retroshare Team <contact@retroshare.cc>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include <system_error>
#include <memory>
#include <jni/jni.hpp>
#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<jni::Object<ErrorConditionWrap>> start(
JNIEnv& env, jni::Class<RetroShareServiceAndroid>& 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<jni::Object<ErrorConditionWrap>> stop(
JNIEnv& env, jni::Class<RetroShareServiceAndroid>& );
struct Context
{
/// JNI parafernalia
static constexpr auto Name() { return "android/content/Context"; }
};
/// Return RetroShare Service Android Context
static jni::Local<jni::Object<Context>> 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<AndroidCoutCerrCatcher> sAndroidCoutCerrCatcher;
};

View File

@ -0,0 +1,70 @@
/*******************************************************************************
* RetroShare JNI utilities *
* *
* libretroshare: retroshare core library *
* *
* Copyright (C) 2021 Gioacchino Mazzurco <gio@eigenlab.org> *
* Copyright (C) 2021 Asociación Civil Altermundi <info@altermundi.net> *
* *
* 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 <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#include "rs_android/rsjni.hpp"
#include "rs_android/retroshareserviceandroid.hpp"
rs_view_ptr<JavaVM> 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<RsJni::AssetHelper>::Singleton(env);
jni::Class<RsJni::ErrorConditionWrap>::Singleton(env);
jni::RegisterNatives(
env, *jni::Class<RetroShareServiceAndroid>::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

View File

@ -0,0 +1,90 @@
/*******************************************************************************
* RetroShare JNI utilities *
* *
* libretroshare: retroshare core library *
* *
* Copyright (C) 2021 Gioacchino Mazzurco <gio@eigenlab.org> *
* Copyright (C) 2021 Asociación Civil Altermundi <info@altermundi.net> *
* *
* 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 <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#pragma once
#include <system_error>
#include <cstdlib>
#include <jni/jni.hpp>
#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<JavaVM> mJvm;
};
namespace jni
{
/** Provides idiomatic way of creating instances via
@code{.cpp}
jni::Make<ErrorConditionWrap>(env, std::error_condition());
@endcode */
jni::Local<jni::Object<RsJni::ErrorConditionWrap>>
MakeAnything(
jni::ThingToMake<RsJni::ErrorConditionWrap>, JNIEnv& env,
const std::error_condition& ec );
}

View File

@ -32,12 +32,12 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <system_error>
#include <iostream>
#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 <openssl/ssl.h>
#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<RetroShareServiceAndroid::Context>::Singleton(env);
auto& applicationInfoClass = jni::Class<ApplicationInfo>::Singleton(env);
auto getApplicationInfo =
contextClass.GetMethod<jni::Object<ApplicationInfo> ()>(
env, "getApplicationInfo" );
auto applicationInfo = androidContext.Call(env, getApplicationInfo);
auto dataDirField = jni::Field<ApplicationInfo, jni::String>(
env, applicationInfoClass, "dataDir" );
jni::Local<jni::String> dataDir = applicationInfo.Get<jni::String>(
env, dataDirField );
basedir = jni::Make<std::string>(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__
}

View File

@ -32,8 +32,9 @@
#endif
#ifdef __ANDROID__
# include <QFile> // To install bdboot.txt
# include <QString> // for QString::fromStdString(...)
# include <jni/jni.hpp>
# 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<RsJni::AssetHelper>::Singleton(env);
static auto copyAsset =
assetHelperClass.GetStaticMethod<
jni::jboolean(jni::Object<AContext>, jni::String, jni::String)>(
env, "copyAsset" );
auto androidContext = RetroShareServiceAndroid::getAndroidContext(env);
jni::jboolean result = assetHelperClass.Call(
env, copyAsset,
androidContext,
jni::Make<jni::String>(env, "values/bdboot.txt"),
jni::Make<jni::String>(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)))
{

View File

@ -1,7 +1,8 @@
/*******************************************************************************
* RetroShare Broadcast Domain Discovery *
* *
* Copyright (C) 2019 Gioacchino Mazzurco <gio@altermundi.net> *
* Copyright (C) 2019-2021 Gioacchino Mazzurco <gio@altermundi.net> *
* Copyright (C) 2019-2021 Asociación Civil Altermundi <info@altermundi.net> *
* *
* 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 <vector>
#include <iostream>
#ifdef __ANDROID__
# include <QtAndroid>
#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<jboolean>("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<AndroidMulticastLock>::Singleton(env);
auto isHeld =
multicastLockClass.GetMethod<jni::jboolean()>(
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<void>("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<AndroidMulticastLock>::Singleton(env);
auto acquire =
multicastLockClass.GetMethod<void()>(
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<void>("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<AndroidMulticastLock>::Singleton(env);
auto release =
multicastLockClass.GetMethod<void()>(
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<jstring>() );
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<jstring>() );
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<AContextTag>;
static auto& contextClass = AContext::Singleton(env);
auto wifiServiceField = jni::StaticField<AContextTag, jni::String>(
env, contextClass, "WIFI_SERVICE");
jni::Local<jni::String> WIFI_SERVICE = contextClass.Get(
env, wifiServiceField );
auto androidContext = RetroShareServiceAndroid::getAndroidContext(env);
auto getSystemService =
contextClass.GetMethod<jni::Object<jni::ObjectTag> (jni::String)>(
env, "getSystemService" );
struct WifiManager
{ static constexpr auto Name() { return "android/net/wifi/WifiManager"; } };
auto& wifiManagerClass = jni::Class<WifiManager>::Singleton(env);
auto wifiManager = jni::Cast<WifiManager>(
env, wifiManagerClass,
androidContext.Call(env, getSystemService, WIFI_SERVICE) );
auto createMulticastLock =
wifiManagerClass.GetMethod<jni::Object<AndroidMulticastLock>(jni::String)>(
env, "createMulticastLock" );
mAndroidWifiMulticastLock = jni::NewGlobal(
env, wifiManager.Call(
env, createMulticastLock,
jni::Make<jni::String>(
env, "RetroShare BroadcastDiscoveryService" ) ) );
return true;
}
#endif // def __ANDROID__
RsBroadcastDiscovery::~RsBroadcastDiscovery() = default;

View File

@ -1,7 +1,8 @@
/*******************************************************************************
* RetroShare Broadcast Domain Discovery *
* *
* Copyright (C) 2019 Gioacchino Mazzurco <gio@altermundi.net> *
* Copyright (C) 2019-2021 Gioacchino Mazzurco <gio@altermundi.net> *
* Copyright (C) 2019-2021 Asociación Civil Altermundi <info@altermundi.net> *
* *
* 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 <udp_discovery_peer.hpp>
#ifdef __ANDROID__
# include <QtAndroidExtras/QAndroidJniObject>
#endif // def __ANDROID__
#include "retroshare/rsbroadcastdiscovery.h"
#include "util/rsthreads.h"
#include "util/rsdebug.h"
#ifdef __ANDROID__
# include <jni/jni.hpp>
# 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<UDC::IpPort, std::string> mDiscoveredData;
RsMutex mDiscoveredDataMutex;
RsPeers& mRsPeers; // TODO: std::shared_ptr<RsPeers> 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<jni::Object<AndroidMulticastLock>> 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)
};

View File

@ -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 #############################

View File

@ -2,9 +2,9 @@
<manifest
package="org.retroshare.service"
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionName="0.6.4" android:versionCode="1"
android:versionName="0.6.6" android:versionCode="1"
android:installLocation="auto">
<application android:name="org.qtproject.qt5.android.bindings.QtApplication" android:hardwareAccelerated="true" android:label="RetroShare" android:icon="@drawable/retroshare_service_128x128">
<application android:label="RetroShare" android:icon="@drawable/retroshare_service_128x128">
<activity
android:name=".RetroShareServiceControlActivity"
android:label="RetroShare" >
@ -14,59 +14,81 @@
</intent-filter>
</activity>
<receiver android:name=".BootCompletedReceiver" android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
<receiver android:name=".AppUpdatedReceiver" android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
</intent-filter>
</receiver>
<!-- For adding service(s) please check:
++ https://wiki.qt.io/AndroidServices -->
<service android:name=".RetroShareServiceAndroid" android:process=":rs" android:label="RetroShare Service" android:exported="true">
<!-- android:exported="true" Added to be able to run the service
++ from adb shell
++ android:process=":rs" is needed to force the service to run on
++ a separate process than the Activity -->
<!-- Background running -->
<meta-data android:name="android.app.background_running" android:value="true"/>
<!-- Background running -->
<![CDATA[
<!-- Qt Application to launch -->
<meta-data android:name="android.app.lib_name" android:value="retroshare-service"/>
<!-- <meta-data android:name="android.app.lib_name" android:value="retroshare-service"/> -->
<!-- Ministro -->
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<!-- Deploy Qt libs as part of package -->
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<!-- Run with local libs -->
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
<meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/>
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
<!-- Messages maps -->
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
<!-- Messages maps -->
<!-- Background running -->
<meta-data android:name="android.app.background_running" android:value="true"/>
<!-- Background running -->
]]>
</service>
<![CDATA[
<!-- G10h4ck: Example on how to start the service at boot -->
<receiver android:name=".BootCompletedReceiver" android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
<!-- G10h4ck: Example on how to restart the service on update -->
<receiver android:name=".AppUpdatedReceiver" android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
</intent-filter>
</receiver>
]]>
</application>
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="18"/>
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
<!-- Added by G10h4ck: Needed permission for network usage -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Added by G10h4ck: Needed to listen for multicast packets, needed for
! broadcast discovery -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<!-- Added by Angesoc: used to access files shared by other apps -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<![CDATA[
<!-- The following comment will be replaced upon deployment with default
++ permissions based on the dependencies of the application.
++ Remove the comment if you do not require these default permissions. -->
@ -76,17 +98,11 @@
++ features based on the dependencies of the application.
++ Remove the comment if you do not require these default features. -->
<!-- %%INSERT_FEATURES -->
]]>
<!-- Added by G10h4ck: Needed permission for autostart at boot -->
<![CDATA[
<!-- Added by G10h4ck: Needed permission for autostart at boot example
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<!-- Added by Angesoc: used to access files shared by other apps -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!-- Added by G10h4ck: Needed permission for network usage -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Added by G10h4ck: Needed to listen for multicast packets, needed for
! broadcast discovery -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
-->
]]>
</manifest>

View File

@ -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);
}
}
*/

View File

@ -0,0 +1 @@
../../../../../../../libretroshare/src/rs_android/org/retroshare/service/AssetHelper.java

View File

@ -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);
}
}
*/

View File

@ -0,0 +1 @@
../../../../../../../libretroshare/src/rs_android/org/retroshare/service/ErrorConditionWrap.java

View File

@ -1,50 +0,0 @@
/*
* RetroShare
* Copyright (C) 2016-2018 Gioacchino Mazzurco <gio@eigenlab.org>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-FileCopyrightText: Retroshare Team <contact@retroshare.cc>
* 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;
}
}

View File

@ -0,0 +1 @@
../../../../../../../libretroshare/src/rs_android/org/retroshare/service/RetroShareServiceAndroid.java

View File

@ -1,6 +1,6 @@
/*
* RetroShare
* Copyright (C) 2016-2018 Gioacchino Mazzurco <gio@altermundi.net>
* Copyright (C) 2016-2021 Gioacchino Mazzurco <gio@altermundi.net>
*
* 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;
}

View File

@ -0,0 +1,29 @@
/*
* RetroShare Service Android
* Copyright (C) 2021 Gioacchino Mazzurco <gio@eigenlab.org>
* Copyright (C) 2021 Asociación Civil Altermundi <info@altermundi.net>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-FileCopyrightText: Retroshare Team <contact@retroshare.cc>
* 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);
}

View File

@ -1,6 +1,7 @@
/*
* RetroShare Service
* Copyright (C) 2016-2019 Gioacchino Mazzurco <gio@eigenlab.org>
* Copyright (C) 2016-2021 Gioacchino Mazzurco <gio@eigenlab.org>
* Copyright (C) 2021 Asociación Civil Altermundi <info@altermundi.net>
*
* 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 <cmath>
#include <csignal>
#include <iomanip>
#include <atomic>
#ifdef __ANDROID__
# include <QAndroidService>
# include <QCoreApplication>
# include <QObject>
# include <QStringList>
# 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<bool> 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
}

View File

@ -1,6 +1,6 @@
# RetroShare service qmake build script
#
# Copyright (C) 2018-2019, Gioacchino Mazzurco <gio@eigenlab.org>
# Copyright (C) 2018-2021, Gioacchino Mazzurco <gio@eigenlab.org>
#
# 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
}

1
supportlibs/jni.hpp Submodule

@ -0,0 +1 @@
Subproject commit 66f73a6aa82367d6ba23e7e842f95dfb33c451d6